Skip to content

Commit 5233b4f

Browse files
authored
Merge pull request #12 from sveltejs/gh-5
Update when items change
2 parents 540c812 + 00e29f3 commit 5233b4f

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"main": "index.js",
88
"scripts": {
99
"build": "rollup -c",
10+
"dev": "rollup -cw",
1011
"prepublishOnly": "npm test",
1112
"test": "node test/runner.js",
1213
"test:browser": "npm run build && serve test/public",

src/VirtualList.html

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<div ref:viewport on:scroll='handleScroll()' style='height: {height};'>
1+
<div ref:viewport on:scroll='refresh()' style='height: {height};'>
22
<div ref:container style='padding-top: {top}px; padding-bottom: {bottom}px;'>
33
{#each visible as item (item.index)}
44
<div class='row'>
@@ -38,23 +38,13 @@
3838
const { viewport, container } = this.refs;
3939
const viewportHeight = viewport.offsetHeight;
4040

41-
const keys = Object.keys(this.options.data).filter(key => key !== 'items' && key !== 'component' && key !== 'itemHeight');
41+
const keys = Object.keys(this.options.data).filter(key => key !== 'items' && key !== 'component' && key !== 'height' && key !== 'itemHeight');
4242
if (keys.length) {
4343
const state = this.get();
4444
keys.forEach(key => {
4545
_props[key] = state[key];
4646
});
4747
this.set({ _props });
48-
49-
this.on('state', ({ changed, current }) => {
50-
if (!keys.some(key => changed[key])) return;
51-
52-
const _props = {};
53-
keys.forEach(key => {
54-
_props[key] = current[key];
55-
});
56-
this.set({ _props });
57-
});
5848
}
5949

6050
this.rows = container.getElementsByClassName('row');
@@ -89,28 +79,44 @@
8979
bottom: (items.length - end) * avg
9080
});
9181
}
82+
83+
this.on('state', ({ changed, previous, current }) => {
84+
if (changed.items || changed.height || changed.itemHeight) {
85+
if (current.itemHeight && (changed.itemHeight || current.items.length !== this.heightMap.length)) {
86+
this.heightMap = current.items.map(() => current.itemHeight);
87+
}
88+
89+
this.refresh();
90+
}
91+
92+
if (keys.some(key => changed[key])) {
93+
const _props = {};
94+
keys.forEach(key => {
95+
_props[key] = current[key];
96+
});
97+
this.set({ _props });
98+
}
99+
});
92100
},
93101

94102
methods: {
95-
handleScroll() {
103+
refresh() {
96104
const { items, start, end, itemHeight } = this.get();
97105
const { offsetHeight, scrollTop } = this.refs.viewport;
98106

99107
let paddingTop = 0;
100108
let offset = 0;
101109
let i = 0;
102110

103-
if (itemHeight) {
104-
if (this.heightMap.length !== items.length) {
105-
this.heightMap = items.map(item => itemHeight);
106-
}
107-
} else {
111+
if (!itemHeight) {
108112
for (let v = 0; v < this.rows.length; v += 1) {
109113
this.heightMap[start + v] = this.rows[v].offsetHeight;
110114
}
111115
}
112116

113117
for (; i < items.length; i += 1) {
118+
if (!(i in this.heightMap)) break;
119+
114120
offset += this.heightMap[i];
115121
if (offset > scrollTop) break;
116122

@@ -120,7 +126,7 @@
120126
const newStart = i++;
121127

122128
for (; i < items.length; i += 1) {
123-
if (offset > scrollTop + offsetHeight) break;
129+
if (offset >= scrollTop + offsetHeight) break;
124130
offset += this.heightMap[i];
125131
}
126132

test/src/index.js

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ test('allows item height to be specified', t => {
8383

8484
list.set({ itemHeight: 50 });
8585

86-
// TODO, run handleScroll when items or itemHeight is updated? Probably not needed.
87-
// t.equal(div.getElementsByClassName('row').length, 3);
86+
t.equal(div.getElementsByClassName('row').length, 3);
8887

8988
list.destroy();
9089
});
@@ -134,5 +133,53 @@ test('props are passed to child component', t => {
134133
list.destroy();
135134
});
136135

136+
test('updates when items change', t => {
137+
const Row = svelte.create(`
138+
<span>{foo}</span>
139+
`);
140+
141+
const list = new VirtualList({
142+
target,
143+
data: {
144+
items: [{ foo: 'bar'}],
145+
component: Row
146+
}
147+
});
148+
149+
t.htmlEqual(target.innerHTML, `
150+
<div style='height: 100%;'>
151+
<div style="padding-top: 0px; padding-bottom: 0px;">
152+
<div class="row">
153+
<span>bar</span>
154+
</div>
155+
</div>
156+
</div>
157+
`);
158+
159+
list.set({
160+
items: [{ foo: 'bar'}, { foo: 'baz'}, { foo: 'qux'}]
161+
});
162+
163+
t.htmlEqual(target.innerHTML, `
164+
<div style='height: 100%;'>
165+
<div style="padding-top: 0px; padding-bottom: 0px;">
166+
<div class="row">
167+
<span>bar</span>
168+
</div>
169+
170+
<div class="row">
171+
<span>baz</span>
172+
</div>
173+
174+
<div class="row">
175+
<span>qux</span>
176+
</div>
177+
</div>
178+
</div>
179+
`);
180+
181+
list.destroy();
182+
});
183+
137184
// this allows us to close puppeteer once tests have completed
138185
window.done = done;

0 commit comments

Comments
 (0)