formstone
Version:
Library of modular front end components.
188 lines (136 loc) • 3.74 kB
JavaScript
import {
extend,
//
select,
iterate,
//
trigger,
//
addClass,
removeClass,
} from './utils.js';
// Class
class CheckPoint {
static #_guid = 1;
static #_defaults = {
// intersect: 'bottom',
offset: '0px',
reverse: false
};
static defaults(options) {
this.#_defaults = extend(true, this.#_defaults, options);
}
static construct(selector, options) {
let targets = select(selector);
iterate(targets, (el) => {
if (!el.CheckPoint) {
new CheckPoint(el, options);
}
});
return targets;
}
//
constructor(el, options) {
if (el.CheckPoint) {
console.warn('Checkpoint: Instance already exists', el);
return;
}
// Parse JSON Options
let optionsData = {};
let dataset = el.dataset;
try {
optionsData = JSON.parse(dataset.checkpointOptions || '{}');
} catch (e) {
console.warn('Checkpoint: Error parsing options JSON', el);
}
// Internal Data
Object.assign(this, extend(true, this.constructor.#_defaults, options || {}, optionsData));
this.el = el;
this.guid = this.constructor.#_guid++;
this.guidClass = `fs-checkpoint-element-${this.guid}`;
this.enabled = false;
this.active = false;
this.hasActived = false;
this.parent = dataset.checkpointParent || null;
this.parentEl = this.parent ? select(this.parent)[0] : null;
this.trigger = dataset.checkpointTrigger || dataset.checkpointContainer || null;
addClass(this.el, this.guidClass);
this.target = this.trigger || `.${this.guidClass}`;
this.targetEl = select(this.target);
// this.intersect = dataset.checkpointIntersect || this.intersect;
this.offset = dataset.checkpointOffset || this.offset;
this.margin = `0px 0px -${this.offset} 0px`;
this.edge = parseInt(this.offset, 10);
this.percent = this.offset.includes('%') ? (this.edge / 100) : null;
this.observer = new IntersectionObserver((entries, observer) => {
iterate(entries, (entry) => {
this.#observe(entry);
});
}, {
root: this.parentEl || document,
threshold: 0,
rootMargin: this.margin
});
this.enable();
el.CheckPoint = this;
}
destroy() {
this.disable();
removeClass(this.el, this.guidClass);
this.observer = null;
this.el.CheckPoint = null;
delete this.el.CheckPoint;
}
//
#observe(entry) {
let height = this.parentEl ? this.parentEl.innerHeight : window.innerHeight;
let edge = height - (this.percent ? (height * this.percent) : this.edge);
if (entry.isIntersecting || entry.boundingClientRect.top < edge) {
this.activate();
} else {
this.deactivate();
}
}
//
enable() {
if (this.enabled) {
return;
}
this.enabled = true;
addClass(this.el, 'fs-checkpoint');
iterate(this.targetEl, (el) => {
this.observer.observe(el);
});
}
disable() {
if (!this.enabled) {
return;
}
this.deactivate();
this.enabled = false;
removeClass(this.el, 'fs-checkpoint', 'fs-checkpoint-active');
iterate(this.targetEl, (el) => {
this.observer.unobserve(el);
});
}
//
activate() {
if (!this.enabled || this.active) {
return;
}
this.active = true;
this.hasActived = true;
addClass(this.el, 'fs-checkpoint-active');
trigger(this.el, 'checkpoint:activate', {});
}
deactivate() {
if (!this.enabled || !this.active || (!this.reverse && this.hasActived)) {
return;
}
this.active = false;
removeClass(this.el, 'fs-checkpoint-active');
trigger(this.el, 'checkpoint:deactivate', {});
}
};
// Export
export default CheckPoint;