/**
* @summary is-in-viewport-mixin
* @version 3.0.0
* @since 1.0.0
* @author Arian Khosravi <arian.khosravi@aofl.com>
*/
import {isInViewport} from './is-in-viewport';
/**
* Mixes the superClass with functions necessary to detect if the element
* is within the visible area of the page.
*
* @memberof module:@aofl/component-utils
* @param {LitElement} superClass
*/
const isInViewportMixin = (superClass) => {
/**
* @memberof module:@aofl/component-utils
* @extends {superClass}
*/
class IsInViewportMixinClass extends superClass {
/**
* Creates an instance of IsInViewportMixinClass.
* @param {*} args
*/
constructor(...args) {
super(...args);
this.trackScrollHosts = [window];
this.isWithinViewport;
this.onceWithinViewport = false;
this.checkInViewport = this.checkInViewport.bind(this);
}
/**
*
* @param {*} args
*/
connectedCallback(...args) {
super.connectedCallback(...args);
this.addListeners();
}
/**
*
*/
checkInViewport() {
const oldIsWithinViewport = this.isWithinViewport;
this.isWithinViewport = this.offsetHeight > 0 && this.offsetWidth > 0 &&
isInViewport(this, this.widthThreshold, this.heightThreshold);
if (!this.onceWithinViewport && this.isWithinViewport === true) {
this.onceWithinViewport = true;
this.firstWithinViewport();
}
if (this.isWithinViewport !== oldIsWithinViewport) {
this.withinViewportUpdated(this.isWithinViewport, oldIsWithinViewport);
}
}
/**
*
*/
firstUpdated() {
this.checkInViewport();
}
/**
*
*/
addListeners() {
let parent = this;
/* istanbul ignore next */
while (parent !== null) {
if (parent.assignedSlot) {
parent = parent.assignedSlot;
} else if (typeof parent.tagName === 'undefined' && typeof parent.host !== 'undefined') {
this.trackScrollHosts.push(parent);
parent = parent.host;
} else if (parent.parentNode) {
parent = parent.parentNode;
} else {
break;
}
}
window.addEventListener('resize', this.checkInViewport);
for (let i = 0; i < this.trackScrollHosts.length; i++) {
this.trackScrollHosts[i].addEventListener('scroll', this.checkInViewport);
}
}
/* istanbul ignore next */
/**
* firstWithinViewport() is invoked when the element scrolls into view for the first time. This
* function should be implemented by the sub class.
*
* @protected
*/
firstWithinViewport() {
}
/* istanbul ignore next */
/**
* withinViewportUpdated() is invoked anytime the element enters or exists the viewport. This
* function should be implemented by the sub class.
*
* @protected
* @param {Boolean} newValue
* @param {Boolean} oldValue
*/
withinViewportUpdated() {}
/**
* When stopiIsInViewportCheck() is invoked it removes the event listeners and stops invoking
* the withinViewportUpdated() function. This is useful when we want to disconnect the event
* listeners and keep the component attached to dom. For example, consider lazy loading images
* with aofl-img. Once the image is loaded it is no longer necessary to check isInViewStatus.
*
* @protected
*/
stopIsInViewportCheck() {
window.removeEventListener('resize', this.checkInViewport);
for (let i = 0; i < this.trackScrollHosts.length; i++) {
this.trackScrollHosts[i].removeEventListener('scroll', this.checkInViewport);
}
}
/**
*
*
* @param {*} args
*/
disconnectedCallback(...args) {
this.stopIsInViewportCheck();
super.disconnectedCallback(...args);
}
}
return IsInViewportMixinClass;
};
export {
isInViewportMixin
};