@ynmstudio/utils
Version:
YNM Utilities for Angular
337 lines (328 loc) • 13.2 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Input, Output, HostBinding, Directive, Injectable, ContentChildren, InjectionToken, inject, HostListener, NgModule } from '@angular/core';
import * as i2 from '@ynmstudio/utils/inviewport';
import { INTERSECTION_OBSERVER_INIT, InViewportDirective } from '@ynmstudio/utils/inviewport';
/**
* A directive used to add an `active` class to a nav item
* when the section is in the viewport
*
* @example
* ```html
* <a snScrollSpyItem for="foo" href="#section1">Section 1</a>
* ```
*
*/
class ScrollSpyItemDirective {
/**
* True if the nav item is the active item in the `items` list
* for `ScrollSpyDirective` instance
*
* @memberof ScrollSpyItemDirective
*/
get getActiveClass() {
return this.active ? this.activeClass : '';
}
set active(state) {
if (this._active === state)
return;
this.activeChange.emit(state);
this._active = state;
}
get active() {
return this._active;
}
set snScrollSpyItemActive(data) {
this.activeClass = data;
}
/**
* Id of section that links navigates to
*
* @readonly
* @memberof ScrollSpyItemDirective
*/
get section() {
return this.href.replace('#', '');
}
/**
* Creates an instance of ScrollSpyItemDirective.
* @memberof ScrollSpyItemDirective
*/
constructor(cdRef) {
this.cdRef = cdRef;
this.activeClass = 'active';
/**
* Emits when the `active` property changes
*/
this.activeChange = new EventEmitter();
this._active = false;
/**
* If true means the section is in the viewport
*
* @memberof ScrollSpyItemDirective
*/
this.inViewport = false;
}
/**
* Manually trigger change detection
*
* @memberof ScrollSpyItemDirective
*/
detectChanges() {
this.cdRef.detectChanges();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyItemDirective, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ScrollSpyItemDirective, isStandalone: true, selector: "[snScrollSpyItem]", inputs: { snScrollSpyItemActive: "snScrollSpyItemActive", for: "for", href: "href" }, outputs: { activeChange: "activeChange" }, host: { properties: { "class": "this.getActiveClass" } }, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyItemDirective, decorators: [{
type: Directive,
args: [{
selector: '[snScrollSpyItem]',
standalone: true
}]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { getActiveClass: [{
type: HostBinding,
args: ['class']
}], activeChange: [{
type: Output
}], snScrollSpyItemActive: [{
type: Input
}], for: [{
type: Input
}], href: [{
type: Input
}] } });
/**
* Service that stores a list of `Spy`'s and the state
* of their nav items `inViewport` and `active` state
*
*/
class ScrollSpyService {
constructor() {
/**
* List of `Spy`'s
*
* @memberof ScrollSpyService
*/
this.spys = [];
/**
* Stores requests to add items to spy when spy hasn't been created
* yet. Once spy has been added then request will be made again.
*
* @memberof ScrollSpyService
*/
this.buffer = [];
}
/**
* Add spy to list of `spys`
*
* @memberof ScrollSpyService
*/
addSpy(id, items) {
this.spys.push({ id, items });
const buffer = this.buffer.filter(i => i.spyId === id);
this.buffer = this.buffer.filter(i => i.spyId !== id);
buffer.forEach(i => this.setSpySectionStatus(i.sectionId, i.spyId, i.inViewport));
}
/**
* Remove spy from list of `spys`
*
* @memberof ScrollSpyService
*/
removeSpy(id) {
const i = this.spys.findIndex(s => s.id === id);
this.spys.splice(i, 1);
}
/**
* Set the `inViewport` status for a spy item then sets the active
* to true for the first item in the list that has `inViewport`
* set to true
*
* @memberof ScrollSpyService
*/
setSpySectionStatus(sectionId, spyId, inViewport) {
const spy = this.spys.find(s => s.id === spyId);
if (!spy) {
this.buffer.push({ sectionId, spyId, inViewport });
return;
}
const item = spy.items.find(i => i.section === sectionId);
if (!item) {
return;
}
item.inViewport = inViewport;
const firstInViewport = spy.items.find(i => i.inViewport);
spy.items.forEach(i => i.active = firstInViewport === i);
if (firstInViewport) {
firstInViewport.detectChanges();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
/**
* Adds `active` class to navigation links when section is in the viewport.
* Used in conjuction with `snScrollItem` directive which should be added
* to anchor links in the nav
*
* @example
* ```
* <ul role="navigation" snScrollSpy id="foo">
* <li><a snScrollSpyItem for="foo" href="#section1">Section 1</a></li>
* <li><a snScrollSpyItem for="foo" href="#section2">Section 2</a></li>
* <li><a snScrollSpyItem for="foo" href="#section3">Section 3</a></li>
* <li><a snScrollSpyItem for="foo" href="#section4">Section 4</a></li>
* </ul>
* ```
*
*/
class ScrollSpyDirective {
/**
* Creates an instance of ScrollSpyDirective.
* @memberof ScrollSpyDirective
*/
constructor(scrollSpySvc) {
this.scrollSpySvc = scrollSpySvc;
}
/**
* Adds spy to list of spys in `ScrollSpyService`
*
* @memberof ScrollSpyDirective
*/
ngAfterViewInit() {
this.scrollSpySvc.addSpy(this.id, this.items);
}
/**
* Remove spy from list of spys when directive is destroyed
*
* @memberof ScrollSpyDirective
*/
ngOnDestroy() {
this.scrollSpySvc.removeSpy(this.id);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyDirective, deps: [{ token: ScrollSpyService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ScrollSpyDirective, isStandalone: true, selector: "[snScrollSpy]", inputs: { id: "id" }, queries: [{ propertyName: "items", predicate: ScrollSpyItemDirective, descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyDirective, decorators: [{
type: Directive,
args: [{
selector: '[snScrollSpy]',
standalone: true
}]
}], ctorParameters: () => [{ type: ScrollSpyService }], propDecorators: { items: [{
type: ContentChildren,
args: [ScrollSpyItemDirective, { descendants: true }]
}], id: [{
type: Input
}] } });
const SCROLL_SPY_INTERSECTION_OBSERVER_INIT = new InjectionToken('Options for IntersectionObserver within ScrollSpy', {
factory: () => ({ rootMargin: '-100px 0px 50px 0px' })
});
/**
* A component to wrap section content within that will update the
* `ScrollSpyService` when it's in view
*
* @example
* ```html
* <section sn-scroll-spy-section id="section1" for="foo">
* ...
* </section>
* ```
*/
class ScrollSpySectionDirective {
/**
* Creates an instance of ScrollSpySectionComponent.
* @memberof ScrollSpySectionComponent
*/
constructor(scrollSpySvc) {
this.scrollSpySvc = scrollSpySvc;
this.inViewportOptions = inject(SCROLL_SPY_INTERSECTION_OBSERVER_INIT, { optional: true });
}
/**
* Updates `ScrollSpy` section when element enters/leaves viewport
*
* @memberof ScrollSpySectionComponent
*/
onInViewportChange(inViewport) {
this.scrollSpySvc.setSpySectionStatus(this.id, this.for, inViewport);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpySectionDirective, deps: [{ token: ScrollSpyService }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ScrollSpySectionDirective, isStandalone: true, selector: "[sn-scroll-spy-section]", inputs: { id: "id", for: "for", inViewportOptions: "inViewportOptions" }, host: { listeners: { "inViewportChange": "onInViewportChange($event)" } }, providers: [
{
provide: INTERSECTION_OBSERVER_INIT,
useExisting: SCROLL_SPY_INTERSECTION_OBSERVER_INIT,
}
], hostDirectives: [{ directive: i2.InViewportDirective, outputs: ["inViewportChange", "inViewportChange"] }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpySectionDirective, decorators: [{
type: Directive,
args: [{
selector: '[sn-scroll-spy-section]',
standalone: true,
hostDirectives: [
{
directive: InViewportDirective,
outputs: ['inViewportChange'],
}
],
providers: [
{
provide: INTERSECTION_OBSERVER_INIT,
useExisting: SCROLL_SPY_INTERSECTION_OBSERVER_INIT,
}
]
}]
}], ctorParameters: () => [{ type: ScrollSpyService }], propDecorators: { id: [{
type: Input
}], for: [{
type: Input
}], onInViewportChange: [{
type: HostListener,
args: ['inViewportChange', ['$event']]
}], inViewportOptions: [{
type: Input
}] } });
/**
* A simple lightweight library for Angular which automatically
* updates links to indicate the currently active section in the viewport
*
*/
class ScrollSpyModule {
/**
* Specify a static method for root module to ensure providers are
* only provided once but allows the module to still be imported
* into other modules without reproviding services.
*
* @memberof ScrollSpyModule
*/
static forRoot() {
return {
ngModule: ScrollSpyModule,
providers: [ScrollSpyService],
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyModule, imports: [InViewportDirective, ScrollSpySectionDirective, ScrollSpyDirective, ScrollSpyItemDirective], exports: [ScrollSpyDirective, ScrollSpyItemDirective, ScrollSpySectionDirective] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyModule }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ScrollSpyModule, decorators: [{
type: NgModule,
args: [{
imports: [InViewportDirective, ScrollSpySectionDirective, ScrollSpyDirective, ScrollSpyItemDirective],
declarations: [],
exports: [ScrollSpyDirective, ScrollSpyItemDirective, ScrollSpySectionDirective],
}]
}] });
/*
* Public API Surface of ngx-scrollspy
*/
/**
* Generated bundle index. Do not edit.
*/
export { SCROLL_SPY_INTERSECTION_OBSERVER_INIT, ScrollSpyDirective, ScrollSpyItemDirective, ScrollSpyModule, ScrollSpySectionDirective, ScrollSpyService };
//# sourceMappingURL=ynmstudio-utils-scrollspy.mjs.map