GithubHelp home page GithubHelp logo

jonasgeiler / svelte-tiny-virtual-list Goto Github PK

View Code? Open in Web Editor NEW
398.0 7.0 23.0 331 KB

A tiny but mighty list virtualization library for Svelte, with zero dependencies ๐Ÿ’ช Supports variable heights/widths, sticky items, scrolling to index, and more!

Home Page: https://svelte-tiny-virtual-list.jonasgeiler.com

License: MIT License

JavaScript 44.74% Svelte 48.12% HTML 0.92% CSS 6.21%
svelte svelte-components virtual list scroll infinite loading component plugin

svelte-tiny-virtual-list's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): update dependency @sveltejs/kit to v2.5.16
  • chore(deps): update dependency beercss to v3.5.8
  • chore(deps): update dependency marked-base-url to v1.1.4
  • chore(deps): update dependency eslint to v8.57.0
  • chore(deps): update dependency eslint-plugin-svelte to v2.40.0
  • chore(deps): update dependency marked-gfm-heading-id to v3.2.0
  • chore(deps): update dependency prettier to v3.3.2
  • chore(deps): update dependency prettier-plugin-svelte to v3.2.4
  • chore(deps): update dependency publint to v0.2.8
  • chore(deps): update dependency svelte-check to v3.8.1
  • chore(deps): update dependency svelte-infinite-loading to v1.4.0
  • chore(deps): update dependency typescript to v5.4.5
  • chore(deps): update dependency vite to v5.3.1
  • chore(deps): update dependency vitest to v1.6.0
  • chore(deps): update pnpm to v9.4.0
  • chore(deps): update dependency eslint to v9
  • chore(deps): update dependency marked to v13
  • chore(deps): update dependency marked-gfm-heading-id to v4
  • chore(deps): update pnpm/action-setup action to v4

Pending Status Checks

These updates await pending status checks. To force their creation now, click the checkbox below.

  • chore(deps): update dependency @sveltejs/adapter-cloudflare to v4.5.0

Detected dependencies

github-actions
.github/workflows/check.yml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • pnpm/action-setup v3.0.0@a3252b78c470c02df07e9d59298aecedc3ccdd6d
  • actions/setup-node v4.0.2@60edb5dd545a775178f52524783378180af0d1f8
npm
package.json
  • @playwright/test 1.44.1
  • @sveltejs/adapter-cloudflare 4.4.1
  • @sveltejs/kit 2.5.15
  • @sveltejs/package 2.3.2
  • @sveltejs/vite-plugin-svelte 3.1.1
  • @types/eslint 8.56.10
  • beercss 3.5.6
  • eslint 8.56.0
  • eslint-config-prettier 9.1.0
  • eslint-plugin-svelte 2.35.1
  • marked 12.0.2
  • marked-base-url 1.1.3
  • marked-gfm-heading-id 3.1.3
  • prettier 3.2.5
  • prettier-plugin-svelte 3.1.2
  • publint 0.1.16
  • svelte 4.2.18
  • svelte-check 3.6.9
  • svelte-infinite-loading 1.3.8
  • tslib 2.6.3
  • typescript 5.3.3
  • vite 5.1.7
  • vitest 1.2.2
  • svelte ^4.0.0
  • node >=18
  • pnpm 9.0.6

  • Check this box to trigger a request for Renovate to run again on this repository

Clarification on footer and header behavior

Hello! I'm looking for some clarification on the footer and header behavior. When adding a footer and header, I'd expect it to

  • not affect the overflow of the parent container. At this moment, when you add a footer and a header, there doesn't appear to be a way for the container to not show a scrollbar.
  • the footer to be positioned immediately after the last element

Here's a REPL illustrating the issue - I'd expect the footer to be visible right after the last number, and for there to be no scrollbar on the container. Is there a way currently to achieve such behavior?

Thanks!

Sveltekit v2 error

Happy belated New Year.

[vite-plugin-svelte] WARNING: The following packages have a svelte field in their package.json but no exports condition for svelte.
[email protected]

With all the changes coming to Svelte 5 and the strong recommendation to upgrade to Sveltekit 2 would you suggest using an alternate component, or do you plan to update the code?

I hope you do, and I thank you for creating such a useful component.

Virtualized Table?

Would it be possible to create a virtualized table like React Virtuoso's table: https://virtuoso.dev/table-fixed-headers/?
Edit: petyosi/react-virtuoso#42 <- this issue is basically the issue I have

For instance, I tried:

<script>
    import VirtualList from "svelte-tiny-virtual-list'';
  
    const data = ['A', 'B', 'C', 'D', 'E', 'F', /* ... */];
</script>
  
<table>
  <thead>
    <tr>
      <td>
        Col 1
      </td>
      <td>
        Col 2
      </td>
    </tr>
  </thead>
  <tbody>
    <VirtualList
        width="100%"
        height={600}
        itemCount={data.length}
        itemSize={50}>
      <div slot="item" let:index let:style {style}>
        <tr>
          <td>{index}</td>
          <td>
            Letter: {data[index]}, Row: #{index}
          </td>
        </tr>
      </div>
    </VirtualList>
  </tbody>
</table>

and the result is:
image

I'm guessing that this is because the 2 divs generated by svelte make the table not recognize the row as a proper row.

Any thoughts/help would be appreciated!

Function for itemSize makes scroll offset not able to go all the way to the bottom

Hi I am working on using the virtual list in a chat type layout and want to always have the offset start at the end of the list. However the elements are dynamically sized and I am using the itemSize as a function. This is working fin but I am not able to scroll all the way to the offset initially unless I overestimate the size of the elements and then the scroll bar gets jittery. Is there any better way to handle this?

ids do not have a stability property.

First, thanks for creating this version of VirtualList. The 'sticky' feature is something I have been wanting.

Now the problem I am facing:

When a scroll list comes from a dynamic database and items are added or removed, a stability property is needed. That is to say for a given item, the loop id should always bind to the same item. From reading the source code, the id right now is item.index which when a database inserts new item, is not stable.

A possible solution:
svelte-virtual-list has the same issue. However I've addressed it in that code very simply and the same could be done here.
First add:

    export let indexing;

and use it in the loop:

        <div class="virtual-list-inner" style={innerStyle}>
		{#each items as item (indexing ? indexing(item.index) : item.index)}
			<slot name="item" style={item.style} index={item.index} />
		{/each}
	</div>

Alternatively, the call to indexing could be done inside the refresh function.

Tiny virtual grid?

Nice work! Any plans to implement svelte-tiny-virtual-grid or perhaps advice on how to go about that?

Issue with scrolling getting stuck in infinite loop

I just gave this a quick try. I have 80 items all with height of 733px. When I scroll down to item number 10, the scroll but starts studdering all over the place and gets into a lock state, CPU goes crazy high. Items are being added/removed infinitly. Have you seen this?

<VirtualList
width={'560px'}
height={Number($globalFeedRef?.getBoundingClientRect().height)}
itemCount={80}
itemSize={733}

<div slot="item" let:index>
	<div style="height: 733px; width: 100%; background-color: azure; border: 1px solid red">{index}</div>
</div>

List with await block works in Svelte 4, but not Svelte 5 Preview

Using scrollToIndex with sticky headers

Background

I'd like to make a virtual table with a few characteristics:

  1. a sticky header row
  2. keyboard shortcuts (โฌ†๏ธ and โฌ‡๏ธ) to change the selected row
  3. auto-scroll to reveal a newly-focused row if it's partly offscreen

Attempts

I tried to implement this in two ways shown in this example code, but each had an issue ๐Ÿ˜…

  1. Put the header in slot="header"

    • Auto-scrolling reveals only part of a row when navigating down. This seems to happen because the header row shifts the other rows down, and auto-scrolling based on scrollToIndex doesn't take that shift into consideration. Based on this comment about invisible elements, I think this is the expected behavior in this case.
  2. Put the header in slot="item"

    • Auto-scrolling reveals only part of a row when navigating up. This seems to happen because the sticky header row hides the topmost row.

Questions

  1. Is there another way to do what I have in mind?
  2. If not, would you be open to a PR that tweaks existing behavior to fit this use case?

Thanks in advance for any insight you can offer! ๐Ÿ™

Dynamic heights?

I tried putting images into the VirtualList. After a bit of tinkering it works, but only until you resize the window. Because the image gets taller when the list gets wider, the VirtualList becomes misaligned. Is there an intended way to fix this or could a feature like this be implemented?

Abuse of function arguments to trigger update

This is just a suggestion regarding how the size recalculation is triggered in this line:

$: if (mounted) recomputeSizes(0, height, width, stickyIndices); // call scroll.reset;

At first glance this looks like a mistake because the function only accepts one argument.
I would instead write it like this:

$: {
  height, width, stickyIndices;
  if (mounted) recomputeSizes(0); // call scroll.reset;
}

REPL example of this approach

Add new item and scroll to it

If I push a new item to the array tracked by VirtualList and change itemCount property on VirtualList component and simultaniosly change scrollToIndex to index of newly added item. Scrolling does not work as expected.

        items = [...items, 'new item']
        scrollToIndex = items.length-1

Expected behavior: Newly created item should appear in the visible area.
Actual behavior: Item does not appear there. Instead I see that the list is scrolled to the last element that existed in the array before I pushed a new Item. So it scrolls to the index items.length-2.

It should be related to the fact that scrollTo function can not jump to the item that is not yet placed.
Workaround: Wait a moment after updating items and before setting new scrollToIndex.

        items = [...items, 'new item']
        // wait a moment before setting scrollToIndex
	await new Promise(res => setTimeout(res, 0))
        scrollToIndex = items.length-1

This is a demo that demonstrates the issue.

Horizontal Infinite Virtual list scroll support

Should the infinite loader be initiated through a dispatched event when last item is reached?

The footer slot is positioned under the list and gets immediately triggered - I tried flex row position on the virtual-list-wrapper.

  <div slot="footer">
    <InfiniteLoading on:infinite={infiniteHandler} />
  </div>

Thanks

How to calculate/recalculate row height with expandable content?

I need to build expandable table, on button click content as sub-row must appear.
I have big problem figuring out how to calculate dynamic height on render (page crashes) and to recalculate height on row expand. Please give me some hint.

<script>
	import VirtualList from 'svelte-tiny-virtual-list';
	let virtualList;
	let expanded = [1, 5];
        let rows = [1,2,3,4,5,6,7,8,9,10]
	let columns = [
		{
			id: 'testCol'
		}
	];

	let heights = [];
	$: console.log(heights);
</script>

<div>
	<div class="w-[500px]">
		<VirtualList
			width="100%"
			height={600}
			itemCount={rows.length}
			itemSize={(i) => heights[i]}
			bind:this={virtualList}
		>
			<div class="" slot="item" let:index let:style {style} bind:clientHeight={heights[index]}>
				{#each columns as column}
					{@const row = rows[index]}
					<div class=" px-sm table-cell">
						<button
							on:click={() => {
								let newArr = [];

								if (!expanded.includes(index)) {
									expanded = [...expanded, index];
									return;
								}

								expanded.map((i) => {
									if (i != index) {
										newArr.push(i);
									}
								});
								expanded = newArr;

								virtualList.recomputeSizes(0);
							}}>Expand trigger button</button
						>
					</div>
				{/each}
				{#if expanded.includes(index)}
					<div class="bg-red-200 w-full">
						<div class="text-right">Expended row content</div>
					</div>
				{/if}
			</div>
		</VirtualList>
	</div>
</div>

Content aware dynamic height.

I'm having a difficult time making dynamic heights work. I am writing a basic chat window with messages of different heights (depending on amount of text inside. Currently I am trying to solve it by binding offsetHeight of the message to itemSize but it causes a lot of jumping and flickering with constant rerendering of components. It is obvious that I am doing something wrong and I would really appreciate any hints on what is wrong or what is the right way of doing it.

Here's REPL link to my code: https://svelte.dev/repl/6a5c26b0b7fa4351a9d30f3f907e00cf?version=3.48.0

ScrollOffset doesnt set scroll position of wrapper on initial render

I cant get the List to render at a certain scroll offset on first render, if the scrollOffset props changes subsequent renders are positioned correctly, but on initial render the wrapper is always positioned at the top. The correct virtual items are rendered (sometimes out of view if the offset is higher enough) and they can be seen in the devtools as being in the right spot.

afterScroll not firing

Hi, the afterScroll is not firing for me, any tips ?

<VirtualList
            width="100%"
            height={400}
            itemCount={$messages.length}
            itemSize={92}
            {scrollOffset}
            {scrollToBehaviour}
            on:afterScroll={() => console.log("well, hello there")}
    >
        <svelte:fragment slot="item" let:index let:style>
            {@const message = $messages[index]}
            <div class="flex gap-x-6 px-8 py-6 relative" {style}>
               // thingy
            </div>
        </svelte:fragment>
</VirtualList>

Unnecessary item creation / destruction

Hi

I have an image viewer that might display thousands of images, and the user can zoom into the images using the mouse-wheel and the 'control' key. I noticed that if the list is large and the current position is not somewhere at the beginning (like the first 100 items? not sure where the limit is), items that are on screen are being rerendered when zooming.

To see what I mean:

https://svelte.dev/repl/69d2cb5333304d29a661c40b2d5808d0?version=4.2.1

If you press 'Ctrl' and scroll while at the beginning of the list, the time of the items do not update. If you scroll down 50% into the list and do the same, the time updates with every "zoom tick". And the time only updates if 'onMount' is called in the rendered Item...why is that? That should not be I think, should it?

Improve performance on scroll & dynamic heights.

I was looking at twitter.com and some thoughts..

Instead of setting top style on the items - if I replace with transform the performance appeared far smoother on older mobiles ie tested on samsung s9 also for large datasets the scrollbar on desktop had less lag in chrome.

transform:translateY(${offset}px)

Also looking at the dynamic heights instead of fixed or having an array of calculated heights..
It looks like twitter looks at previous sibling item element and any changes to the height of that element - auto changes all the following next sibling items top position so that height does not need to be defined and is horizontally responsive - I'm sure there is more logic to calculate full initial size and resize and scroll position this was just an initial observation as twitters approach appears very similar.

Error when initializing virtual list with defined scrollToIndex

When property scrollToIndex has value, it result in following error. See REPL

can't access lexical declaration 'state' before initialization

This is due initialization check not expecting scrollToIndex can have initial value;

Fix is easy - add check also for items.length for state.offset initialization:

// VirtualList.svelte: 62
let state = {
  offset:             scrollOffset || (scrollToIndex != null && items.length && getOffsetForIndex(scrollToIndex)) || 0,
  scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED,
};

<VirtualList> is not a valid SSR component.

<VirtualList> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules
Error: <VirtualList> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules

sveltejs/kit 1.0.0-next.396

Enable CodeQL

Enable CodeQL in the settings after releasing 3.0.0

If items.length changes to zero (e.g. in the case of filtering), a fatal error results

	getSizeAndPositionForIndex(index) {
		if (index < 0 || index >= this.itemCount) {
			throw Error(
				`Requested index ${index} is outside of range 0..${this.itemCount}`,
			);
		}

		return this.justInTime
			? this.getJustInTimeSizeAndPositionForIndex(index)
			: this.itemSizeAndPositionData[index];
	}

I have a list of items but if filtering on terms for instance and there are no results for the entered term, then the above code generates an error when used in conjunction with scrollToIndex.

    beforeUpdate(async () => {
        await tick();
        scrollToIndex = filteredItems.length;
    });

Wrapper offset after scrollToIndex is modified

Hey there,

I was wondering if there is a way to get the result of getWrapperOffset() after scrollToIndex prop is modified? After regular scrolling the afterScroll event is dispatched and the offset can be obtained that way. However, the event is not dispatched after scrollToIndex is modified.

Thanks for your time,
winterSteve25

estimatedItemSize - details

Hi

first of all, many thanks for that great component! :-) Works really nice and smooth.

I do have a question about the 'estimateItemSize' though:
I was under the impression that this was used to calculate the total scroll-height. So,...that it should basically match itemCount * itemSize in case of equal height items.

If that's right, I also thought that this would prevent that growing scrollbar during scrolling. It seems that as items are being recomputed, my scrollbar grows. So I tried to use estimatedItemSize as described above. But that gives me another weird behavior (scrolling to the bottom after a few initial correctly scrolled items). So maybe I got that totally wrong,...:-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.