Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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);
}
}