Skip to content

Commit 342f007

Browse files
authored
Merge pull request #13 from sveltejs/zero-items
Handle changes from zero to some items
2 parents 5233b4f + 62bc6ac commit 342f007

File tree

3 files changed

+142
-43
lines changed

3 files changed

+142
-43
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
"pretest": "npm run build"
1515
},
1616
"devDependencies": {
17-
"faucet": "^0.0.1",
1817
"port-authority": "^1.0.3",
1918
"puppeteer": "^1.2.0",
2019
"rollup": "^0.58.2",
@@ -23,6 +22,7 @@
2322
"rollup-plugin-svelte": "^4.1.0",
2423
"serve": "^6.5.3",
2524
"svelte": "^2.1.0",
25+
"tap-diff": "^0.1.1",
2626
"tap-dot": "^1.0.5",
2727
"tape-modern": "^1.0.0"
2828
},

src/VirtualList.html

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@
3434
},
3535

3636
oncreate() {
37-
const { items, _props, itemHeight } = this.get();
38-
const { viewport, container } = this.refs;
39-
const viewportHeight = viewport.offsetHeight;
37+
const { items, _props } = this.get();
38+
const { container } = this.refs;
4039

4140
const keys = Object.keys(this.options.data).filter(key => key !== 'items' && key !== 'component' && key !== 'height' && key !== 'itemHeight');
4241
if (keys.length) {
@@ -47,45 +46,31 @@
4746
this.set({ _props });
4847
}
4948

49+
this.heightMap = [];
5050
this.rows = container.getElementsByClassName('row');
5151

52-
if (itemHeight) {
53-
this.heightMap = items.map(item => itemHeight);
54-
this.set({
55-
end: Math.min(items.length, Math.ceil(viewportHeight / itemHeight)),
56-
bottom: items.length * itemHeight
57-
});
58-
} else {
59-
this.heightMap = [];
60-
61-
let height = 0;
62-
let i = 0;
63-
64-
while (height < viewportHeight && i < items.length) {
65-
this.set({ end: i + 1 });
66-
67-
const rowHeight = this.heightMap[i] = this.rows[i].offsetHeight;
68-
height += rowHeight;
69-
70-
i += 1;
71-
}
72-
73-
const end = i;
74-
const avg = Math.round(height / i);
75-
76-
for (; i < items.length; i += 1) this.heightMap[i] = avg;
77-
78-
this.set({
79-
bottom: (items.length - end) * avg
80-
});
52+
if (items.length > 0) {
53+
this.initialise();
8154
}
8255

8356
this.on('state', ({ changed, previous, current }) => {
8457
if (changed.items || changed.height || changed.itemHeight) {
85-
if (current.itemHeight && (changed.itemHeight || current.items.length !== this.heightMap.length)) {
58+
if (current.itemHeight && (changed.itemHeight || current.items.length > this.heightMap.length)) {
8659
this.heightMap = current.items.map(() => current.itemHeight);
8760
}
8861

62+
else if (current.items.length > this.heightMap.length) {
63+
if (this.heightMap.length === 0) {
64+
this.initialise();
65+
} else {
66+
let height = 0;
67+
let i = 0;
68+
for (; i < this.heightMap.length; i += 1) height += this.heightMap[i];
69+
const avg = height / this.heightMap.length;
70+
for (; i < current.items.length; i += 1) this.heightMap[i] = avg;
71+
}
72+
}
73+
8974
this.refresh();
9075
}
9176

@@ -100,6 +85,41 @@
10085
},
10186

10287
methods: {
88+
initialise() {
89+
const { items, itemHeight } = this.get();
90+
const { viewport } = this.refs;
91+
const viewportHeight = viewport.offsetHeight;
92+
93+
if (itemHeight) {
94+
this.heightMap = items.map(item => itemHeight);
95+
this.set({
96+
end: Math.min(items.length, Math.ceil(viewportHeight / itemHeight)),
97+
bottom: items.length * itemHeight
98+
});
99+
} else {
100+
let height = 0;
101+
let i = 0;
102+
103+
while (height < viewportHeight && i < items.length) {
104+
this.set({ end: i + 1 });
105+
106+
const rowHeight = this.heightMap[i] = this.rows[i].offsetHeight;
107+
height += rowHeight;
108+
109+
i += 1;
110+
}
111+
112+
const end = i;
113+
const avg = Math.round(height / i);
114+
115+
for (; i < items.length; i += 1) this.heightMap[i] = avg;
116+
117+
this.set({
118+
bottom: (items.length - end) * avg
119+
});
120+
}
121+
},
122+
103123
refresh() {
104124
const { items, start, end, itemHeight } = this.get();
105125
const { offsetHeight, scrollTop } = this.refs.viewport;

test/src/index.js

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,45 @@ import { assert, test, done } from 'tape-modern';
55
// setup
66
const target = document.querySelector('main');
77

8+
function indent(node, spaces) {
9+
if (node.childNodes.length === 0) return;
10+
11+
if (node.childNodes.length > 1 || node.childNodes[0].nodeType !== 3) {
12+
const first = node.childNodes[0];
13+
const last = node.childNodes[node.childNodes.length - 1];
14+
15+
const head = `\n${spaces} `;
16+
const tail = `\n${spaces}`;
17+
18+
if (first.nodeType === 3) {
19+
first.data = `${head}${first.data}`;
20+
} else {
21+
node.insertBefore(document.createTextNode(head), first);
22+
}
23+
24+
if (last.nodeType === 3) {
25+
last.data = `${last.data}${tail}`;
26+
} else {
27+
node.appendChild(document.createTextNode(tail));
28+
}
29+
30+
let lastType = null;
31+
for (let i = 0; i < node.childNodes.length; i += 1) {
32+
const child = node.childNodes[i];
33+
if (child.nodeType === 1) {
34+
indent(node.childNodes[i], `${spaces} `);
35+
36+
if (lastType === 1) {
37+
node.insertBefore(document.createTextNode(head), child);
38+
i += 1;
39+
}
40+
}
41+
42+
lastType = child.nodeType;
43+
}
44+
}
45+
}
46+
847
function normalize(html) {
948
const div = document.createElement('div');
1049
div.innerHTML = html
@@ -15,6 +54,8 @@ function normalize(html) {
1554
.replace(/>\s+/g, '>')
1655
.replace(/\s+</g, '<');
1756

57+
indent(div, '');
58+
1859
div.normalize();
1960
return div.innerHTML;
2061
}
@@ -135,22 +176,23 @@ test('props are passed to child component', t => {
135176

136177
test('updates when items change', t => {
137178
const Row = svelte.create(`
138-
<span>{foo}</span>
179+
<div style="height: 80px;">{foo}</div>
139180
`);
140181

141182
const list = new VirtualList({
142183
target,
143184
data: {
144185
items: [{ foo: 'bar'}],
145-
component: Row
186+
component: Row,
187+
height: '100px'
146188
}
147189
});
148190

149191
t.htmlEqual(target.innerHTML, `
150-
<div style='height: 100%;'>
192+
<div style='height: 100px;'>
151193
<div style="padding-top: 0px; padding-bottom: 0px;">
152194
<div class="row">
153-
<span>bar</span>
195+
<div style="height: 80px;">bar</div>
154196
</div>
155197
</div>
156198
</div>
@@ -161,18 +203,55 @@ test('updates when items change', t => {
161203
});
162204

163205
t.htmlEqual(target.innerHTML, `
164-
<div style='height: 100%;'>
165-
<div style="padding-top: 0px; padding-bottom: 0px;">
206+
<div style='height: 100px;'>
207+
<div style="padding-top: 0px; padding-bottom: 80px;">
166208
<div class="row">
167-
<span>bar</span>
209+
<div style="height: 80px;">bar</div>
168210
</div>
169211
170212
<div class="row">
171-
<span>baz</span>
213+
<div style="height: 80px;">baz</div>
172214
</div>
215+
</div>
216+
</div>
217+
`);
218+
219+
list.destroy();
220+
});
173221

222+
test('updates when items change from an empty list', t => {
223+
const Row = svelte.create(`
224+
<div style="height: 80px;">{foo}</div>
225+
`);
226+
227+
const list = new VirtualList({
228+
target,
229+
data: {
230+
items: [],
231+
component: Row,
232+
height: '100px'
233+
}
234+
});
235+
236+
t.htmlEqual(target.innerHTML, `
237+
<div style='height: 100px;'>
238+
<div style="padding-top: 0px; padding-bottom: 0px;"></div>
239+
</div>
240+
`);
241+
242+
list.set({
243+
items: [{ foo: 'bar'}, { foo: 'baz'}, { foo: 'qux'}]
244+
});
245+
246+
t.htmlEqual(target.innerHTML, `
247+
<div style='height: 100px;'>
248+
<div style="padding-top: 0px; padding-bottom: 80px;">
174249
<div class="row">
175-
<span>qux</span>
250+
<div style="height: 80px;">bar</div>
251+
</div>
252+
253+
<div class="row">
254+
<div style="height: 80px;">baz</div>
176255
</div>
177256
</div>
178257
</div>

0 commit comments

Comments
 (0)