Skip to content
Snippets Groups Projects
index.js 2.66 KiB
Newer Older
insert's avatar
insert committed
class _vDynamicUpdateCycle {
	constructor(app, component, load, total, limit = 100) {
		this.app = app;
		this.component = app.$refs[component];
		this.load = load;
		this.total = Math.ceil(total / limit);
		this.limit = limit;

		this.states = [];
		this.loadQueue = [];
		this.canUpdate = true;

		for (let i=0;i<this.total;i++) {
			this.states.push({loaded: false, loading: false});
			this.component.sections.push(false);
		}

		// wait for DOM
		$(document).ready(() => this.update());

		let delay;
		document.addEventListener('scroll', () => {
			clearTimeout(delay);
			delay = setTimeout(() => this.update(), 25);
		});
	}
	populate(x, e = false) {
		let y = [];
		for (let i=0;i<x;i++) y.push(e);
		return y;
	}
	updateElm(i, v) {
		return new Promise(resolve => {
			this.load(i, this.limit).then(data => {
				this.component.sections.splice(i, 1, data);
				this.states[i].loaded = true;
				resolve();
			});
		});
	}
	process(n) {
		this.updateElm(n[0], n[1]).then(() => {
			if (this.loadQueue.length > 0)
				return this.process(this.loadQueue.shift());
			this.canUpdate = true;
		});
	}
	update() {
		let entries = $('.section');
		for (let i=0;i<entries.length;i++) {
			let v = entries[i];
			if (!this.states[i].loading) {
				if (this.visible(entries[i])) {
					this.component.sections.splice(i, 1, this.populate(this.limit));
					this.states[i].loading = true;
				}
			}
			if (!this.states[i].loaded) {
				if (this.visible(entries[i])) {
					this.component.sections.splice(i, 1, this.populate(this.limit));
					if (this.loadQueue.indexOf([i, v] == -1)) this.loadQueue.push([i, v]);
				}
			}
		};
		if (this.loadQueue.length == 0 || !this.canUpdate) return;
		this.canUpdate = false;
		this.process(this.loadQueue.shift());
	}
	visible(elm) {
		let rect = elm.getBoundingClientRect();
		let viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
		return !(rect.bottom < 0 || rect.top - viewHeight >= 0);
	}
}

Vue.component('dynamic-list', {
	data: () => {
		return {
			sections: []
		}
	},
	template: `<div class="dynamic-list">
	<div v-for="section in sections" class="section">
		<div v-if="section" v-for="entry in section" class="entry">
			<div v-if="entry" class="loaded">
				<slot name="entry" v-bind:entry="entry"></slot>
			</div>
			<div v-if="!entry" class="loadbar">
				<slot name="unloaded"></slot>
			</div>
		</div>
	</div>
</div>`
});

class vDynamicList {
	constructor(app, options) {
		this.ref = options.ref || 'list';
		this.onLoad = options.onLoad || ($=>{});
		this.elements = options.elements || 0;
		this.limit = options.limit || 100;
		this.cycle = new _vDynamicUpdateCycle(app, this.ref, this.onLoad, this.elements, this.limit);
	}
}