Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate height map when items are deleted #44

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 36 additions & 29 deletions VirtualList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
export let height = '100%';
export let itemHeight = undefined;

let foo;

// read-only, but visible to consumers via bind:start
export let start = 0;
export let end = 0;
Expand All @@ -30,103 +28,114 @@
});

// whenever `items` changes, invalidate the current heightmap
$: if (mounted) refresh(items, viewport_height, itemHeight);
$: if (mounted) invalidateHeightMap(items);


async function invalidateHeightMap(items) {

if (items.length === 0) {
start = 0
end = 0
return;
top = 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 40 to 42 is unreachable.

bottom = 0
height_map = []

}
if (start > items.length - 1) {
start = items.length - 1
top = 0
bottom = 0
height_map = []
}
if (end > items.length - 1) {
end = items.length - 1
top = 0
bottom = 0
height_map = []
}

await refresh(items, viewport_height, itemHeight);
await tick();
await handle_scroll();
}

async function refresh(items, viewport_height, itemHeight) {

const { scrollTop } = viewport;

await tick(); // wait until the DOM is up to date
// if items has changed, we have to check to see if start and end are still in range

await tick(); // wait until the DOM is up to date
let content_height = top - scrollTop;

let i = start;

while (content_height < viewport_height && i < items.length) {
let row = rows[i - start];

if (!row) {
end = i + 1;
await tick(); // render the newly visible row
row = rows[i - start];
}

const row_height = height_map[i] = itemHeight || row.offsetHeight;
content_height += row_height;
i += 1;
}

end = i;

const remaining = items.length - end;
average_height = (top + content_height) / end;

bottom = remaining * average_height;
height_map.length = items.length;

}

async function handle_scroll() {
const { scrollTop } = viewport;

const old_start = start;

for (let v = 0; v < rows.length; v += 1) {
height_map[start + v] = itemHeight || rows[v].offsetHeight;
}

let i = 0;
let y = 0;

while (i < items.length) {
const row_height = height_map[i] || average_height;
if (y + row_height > scrollTop) {
start = i;
top = y;

break;
}

y += row_height;
i += 1;
}

while (i < items.length) {
y += height_map[i] || average_height;
i += 1;

if (y > scrollTop + viewport_height) break;
}

end = i;

const remaining = items.length - end;
average_height = y / end;

while (i < items.length) height_map[i++] = average_height;
bottom = remaining * average_height;

// prevent jumping if we scrolled up into unknown territory
if (start < old_start) {
await tick();

let expected_height = 0;
let actual_height = 0;

for (let i = start; i < old_start; i +=1) {
if (rows[i - start]) {
expected_height += height_map[i];
actual_height += itemHeight || rows[i - start].offsetHeight;
}
}

const d = actual_height - expected_height;
viewport.scrollTo(0, scrollTop + d);
}

// TODO if we overestimated the space these
// rows would occupy we may need to add some
// more. maybe we can just call handle_scroll again?
}

// trigger initial refresh
onMount(() => {
rows = contents.getElementsByTagName('svelte-virtual-list-row');
Expand All @@ -141,11 +150,9 @@
-webkit-overflow-scrolling:touch;
display: block;
}

svelte-virtual-list-contents, svelte-virtual-list-row {
display: block;
}

svelte-virtual-list-row {
overflow: hidden;
}
Expand All @@ -163,7 +170,7 @@
>
{#each visible as row (row.index)}
<svelte-virtual-list-row>
<slot item={row.data}>Missing template</slot>
<slot index={row.index} item={row.data}>Missing template</slot>
</svelte-virtual-list-row>
{/each}
</svelte-virtual-list-contents>
Expand Down