@thisissoon/angular-scrollspy
Version:
A simple lightweight library for Angular which automatically updates links to indicate the currently active section in the viewport
328 lines (319 loc) • 9.51 kB
JavaScript
import { Directive, Input, HostBinding, ChangeDetectorRef, Injectable, ContentChildren, Component, NgModule } from '@angular/core';
import { InViewportModule } from '@thisissoon/angular-inviewport';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* 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 {
/**
* Creates an instance of ScrollSpyItemDirective.
* \@memberof ScrollSpyItemDirective
* @param {?} cdRef
*/
constructor(cdRef) {
this.cdRef = cdRef;
/**
* True if the nav item is the active item in the `items` list
* for `ScrollSpyDirective` instance
*
* \@memberof ScrollSpyItemDirective
*/
this.active = false;
/**
* If true means the section is in the viewport
*
* \@memberof ScrollSpyItemDirective
*/
this.inViewport = false;
}
/**
* Id of section that links navigates to
*
* \@readonly
* \@memberof ScrollSpyItemDirective
* @return {?}
*/
get section() {
return this.href.replace('#', '');
}
/**
* Manually trigger change detection
*
* \@memberof ScrollSpyItemDirective
* @return {?}
*/
detectChanges() {
this.cdRef.detectChanges();
}
}
ScrollSpyItemDirective.decorators = [
{ type: Directive, args: [{
selector: '[snScrollSpyItem]',
},] }
];
/** @nocollapse */
ScrollSpyItemDirective.ctorParameters = () => [
{ type: ChangeDetectorRef }
];
ScrollSpyItemDirective.propDecorators = {
active: [{ type: HostBinding, args: ['class.active',] }],
for: [{ type: Input }],
href: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* 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
* @param {?} id
* @param {?} items
* @return {?}
*/
addSpy(id, items) {
this.spys.push({ id, items });
/** @type {?} */
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
* @param {?} id
* @return {?}
*/
removeSpy(id) {
/** @type {?} */
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
* @param {?} sectionId
* @param {?} spyId
* @param {?} inViewport
* @return {?}
*/
setSpySectionStatus(sectionId, spyId, inViewport) {
/** @type {?} */
const spy = this.spys.find(s => s.id === spyId);
if (!spy) {
this.buffer.push({ sectionId, spyId, inViewport });
return;
}
/** @type {?} */
const item = spy.items.find(i => i.section === sectionId);
if (!item) {
return;
}
item.inViewport = inViewport;
/** @type {?} */
const firstInViewport = spy.items.filter(i => i.inViewport)[0];
spy.items.forEach(i => (i.active = false));
if (firstInViewport) {
firstInViewport.active = true;
firstInViewport.detectChanges();
}
}
}
ScrollSpyService.decorators = [
{ type: Injectable }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* 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
* @param {?} scrollSpySvc
*/
constructor(scrollSpySvc) {
this.scrollSpySvc = scrollSpySvc;
}
/**
* Adds spy to list of spys in `ScrollSpyService`
*
* \@memberof ScrollSpyDirective
* @return {?}
*/
ngAfterViewInit() {
this.scrollSpySvc.addSpy(this.id, this.items);
}
/**
* Remove spy from list of spys when directive is destroyed
*
* \@memberof ScrollSpyDirective
* @return {?}
*/
ngOnDestroy() {
this.scrollSpySvc.removeSpy(this.id);
}
}
ScrollSpyDirective.decorators = [
{ type: Directive, args: [{
selector: '[snScrollSpy]',
},] }
];
/** @nocollapse */
ScrollSpyDirective.ctorParameters = () => [
{ type: ScrollSpyService }
];
ScrollSpyDirective.propDecorators = {
items: [{ type: ContentChildren, args: [ScrollSpyItemDirective,] }],
id: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* A component to wrap section content within that will update the
* `ScrollSpyService` when it's in view
*
* \@example
* ```html
* <sn-scroll-spy-section id="section1" for="foo">
* ...
* </sn-scroll-spy-section>
* ```
*/
class ScrollSpySectionComponent {
/**
* Creates an instance of ScrollSpySectionComponent.
* \@memberof ScrollSpySectionComponent
* @param {?} scrollSpySvc
*/
constructor(scrollSpySvc) {
this.scrollSpySvc = scrollSpySvc;
}
/**
* Updates `ScrollSpy` section when element enters/leaves viewport
*
* \@memberof ScrollSpySectionComponent
* @param {?} inViewport
* @return {?}
*/
onInViewportChange(inViewport) {
this.scrollSpySvc.setSpySectionStatus(this.id, this.for, inViewport);
}
}
ScrollSpySectionComponent.decorators = [
{ type: Component, args: [{
selector: 'sn-scroll-spy-section',
template: "<div\n class=\"sn-hidden\"\n snInViewport\n (inViewportChange)=\"onInViewportChange($event)\">\n</div>\n<ng-content></ng-content>\n",
styles: [":host{display:block;position:relative}.sn-hidden{bottom:0;left:0;opacity:0;position:absolute;right:0;top:0;z-index:-1}"]
}] }
];
/** @nocollapse */
ScrollSpySectionComponent.ctorParameters = () => [
{ type: ScrollSpyService }
];
ScrollSpySectionComponent.propDecorators = {
id: [{ type: Input }],
for: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const directives = [ScrollSpyDirective, ScrollSpyItemDirective];
/** @type {?} */
const components = [ScrollSpySectionComponent];
/** @type {?} */
const providers = [ScrollSpyService];
/**
* 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
* @return {?}
*/
static forRoot() {
return {
ngModule: ScrollSpyModule,
providers: [...providers],
};
}
}
ScrollSpyModule.decorators = [
{ type: NgModule, args: [{
imports: [InViewportModule],
declarations: [...directives, ...components],
exports: [...directives, ...components],
},] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { ScrollSpyDirective, ScrollSpyItemDirective, ScrollSpySectionComponent, ScrollSpyService, ScrollSpyModule };
//# sourceMappingURL=thisissoon-angular-scrollspy.js.map