Skip to content

Commit 687137e

Browse files
committed
update for v3
1 parent d5b72ba commit 687137e

File tree

5 files changed

+195
-205
lines changed

5 files changed

+195
-205
lines changed

.eslintrc.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"extends": "eslint:recommended",
3+
"parserOptions": {
4+
"ecmaVersion": 9,
5+
"sourceType": "module"
6+
},
7+
"plugins": ["svelte3"],
8+
"settings": {
9+
"svelte3/extensions": ["html"]
10+
},
11+
"env": {
12+
"browser": true
13+
}
14+
}

VirtualList.html

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
<script>
2+
import { onMount, tick } from 'svelte';
3+
4+
// props
5+
export let items;
6+
export let height = '100%';
7+
export let itemHeight;
8+
9+
let foo;
10+
11+
// read-only, but visible to consumers via bind:start
12+
export let start = 0;
13+
export let end = 0;
14+
15+
// local state
16+
let height_map = [];
17+
let rows;
18+
let viewport;
19+
let contents;
20+
let viewport_height = 0;
21+
let visible;
22+
let mounted;
23+
24+
let top = 0;
25+
let bottom = 0;
26+
let average_height;
27+
28+
$: visible = items.slice(start, end).map((data, i) => {
29+
return { index: i + start, data };
30+
});
31+
32+
// whenever `items` changes, invalidate the current heightmap
33+
$: if (mounted) refresh(items, viewport_height);
34+
35+
async function refresh(items, viewport_height) {
36+
const { scrollTop } = viewport;
37+
38+
await tick(); // wait until the DOM is up to date
39+
40+
let content_height = top - scrollTop;
41+
let i = start;
42+
43+
while (content_height < viewport_height && i < items.length) {
44+
let row = rows[i - start];
45+
46+
if (!row) {
47+
end = i + 1;
48+
await tick(); // render the newly visible row
49+
row = rows[i - start];
50+
}
51+
52+
const row_height = height_map[i] = itemHeight || row.offsetHeight;
53+
content_height += row_height;
54+
i += 1;
55+
}
56+
57+
end = i;
58+
59+
const remaining = items.length - end;
60+
average_height = (top + content_height) / end;
61+
62+
bottom = remaining * average_height;
63+
height_map.length = items.length;
64+
}
65+
66+
async function handle_scroll() {
67+
const { scrollTop } = viewport;
68+
69+
const old_start = start;
70+
71+
for (let v = 0; v < rows.length; v += 1) {
72+
height_map[start + v] = itemHeight || rows[v].offsetHeight;
73+
}
74+
75+
let i = 0;
76+
let y = 0;
77+
78+
while (i < items.length) {
79+
const row_height = height_map[i] || average_height;
80+
if (y + row_height > scrollTop) {
81+
start = i;
82+
top = y;
83+
84+
break;
85+
}
86+
87+
y += row_height;
88+
i += 1;
89+
}
90+
91+
while (i < items.length) {
92+
y += height_map[i] || average_height;
93+
i += 1;
94+
95+
if (y > scrollTop + viewport_height) break;
96+
}
97+
98+
end = i;
99+
100+
const remaining = items.length - end;
101+
average_height = y / end;
102+
103+
while (i < items.length) height_map[i++] = average_height;
104+
bottom = remaining * average_height;
105+
106+
// prevent jumping if we scrolled up into unknown territory
107+
if (start < old_start) {
108+
await tick();
109+
110+
let expected_height = 0;
111+
let actual_height = 0;
112+
113+
for (let i = start; i < old_start; i +=1) {
114+
if (rows[i - start]) {
115+
expected_height += height_map[i];
116+
actual_height += itemHeight || rows[i - start].offsetHeight;
117+
}
118+
}
119+
120+
const d = actual_height - expected_height;
121+
viewport.scrollTo(0, scrollTop + d);
122+
}
123+
124+
// TODO if we overestimated the space these
125+
// rows would occupy we may need to add some
126+
// more. maybe we can just call handle_scroll again?
127+
}
128+
129+
// trigger initial refresh
130+
onMount(() => {
131+
rows = contents.getElementsByTagName('svelte-virtual-list-row');
132+
mounted = true;
133+
});
134+
</script>
135+
136+
<style>
137+
svelte-virtual-list-viewport {
138+
position: relative;
139+
overflow-y: auto;
140+
-webkit-overflow-scrolling:touch;
141+
display: block;
142+
}
143+
144+
svelte-virtual-list-contents, svelte-virtual-list-row {
145+
display: block;
146+
}
147+
148+
svelte-virtual-list-row {
149+
overflow: hidden;
150+
}
151+
</style>
152+
153+
<svelte-virtual-list-viewport
154+
bind:this={viewport}
155+
bind:offsetHeight={viewport_height}
156+
on:scroll={handle_scroll}
157+
style="height: {height};"
158+
>
159+
<svelte-virtual-list-contents
160+
bind:this={contents}
161+
style="padding-top: {top}px; padding-bottom: {bottom}px;"
162+
>
163+
{#each visible as row (row.index)}
164+
<svelte-virtual-list-row>
165+
<slot item={row.data}>Missing template</slot>
166+
</svelte-virtual-list-row>
167+
{/each}
168+
</svelte-virtual-list-contents>
169+
</svelte-virtual-list-viewport>

package.json

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,25 @@
22
"name": "@sveltejs/svelte-virtual-list",
33
"version": "2.2.1",
44
"description": "A <VirtualList> component for Svelte apps",
5-
"svelte": "src/VirtualList.html",
6-
"module": "index.mjs",
7-
"main": "index.js",
5+
"svelte": "VirtualList.html",
86
"scripts": {
9-
"build": "rollup -c",
10-
"dev": "rollup -cw",
117
"prepublishOnly": "npm test",
128
"test": "node test/runner.js",
139
"test:browser": "npm run build && serve test/public",
14-
"pretest": "npm run build"
10+
"pretest": "npm run build",
11+
"lint": "eslint src/VirtualList.html"
1512
},
1613
"devDependencies": {
14+
"eslint": "^5.12.1",
15+
"eslint-plugin-svelte3": "git+https://github.com/sveltejs/eslint-plugin-svelte3.git",
1716
"port-authority": "^1.0.5",
1817
"puppeteer": "^1.9.0",
19-
"rollup": "^0.66.6",
18+
"rollup": "^1.1.2",
2019
"rollup-plugin-commonjs": "^9.2.0",
21-
"rollup-plugin-node-resolve": "^3.4.0",
22-
"rollup-plugin-svelte": "^4.3.2",
20+
"rollup-plugin-node-resolve": "^4.0.0",
21+
"rollup-plugin-svelte": "^5.0.1",
2322
"sirv": "^0.2.2",
24-
"svelte": "^2.13.5",
23+
"svelte": "^3.0.0-beta.2",
2524
"tap-diff": "^0.1.1",
2625
"tap-dot": "^2.0.0",
2726
"tape-modern": "^1.1.1"

rollup.config.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ import pkg from './package.json';
55

66
export default [
77
{
8-
input: 'src/VirtualList.html',
9-
output: [
10-
{ file: pkg.module, 'format': 'es' },
11-
{ file: pkg.main, 'format': 'umd', name: 'VirtualList' }
12-
],
8+
input: 'test/src/index.js',
9+
output: { file: 'test/public/bundle.js', 'format': 'iife' },
1310
plugins: [
1411
resolve(),
12+
commonjs(),
1513
svelte()
1614
]
1715
},

0 commit comments

Comments
 (0)