You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

svelte-tiny-virtual-list

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte-tiny-virtual-list - npm Package Compare versions

Comparing version

to
4.0.0-rc.1

2

dist/index.js

@@ -1,1 +0,1 @@

export { default as default } from './VirtualList.svelte';
export { default } from './VirtualList.svelte';

@@ -184,3 +184,3 @@ /*

if (size == null || isNaN(size)) {
if (size == null || Number.isNaN(size)) {
throw Error(`Invalid size returned for index ${i} of value ${size}`);

@@ -332,3 +332,3 @@ }

findNearestItem(offset) {
if (isNaN(offset)) {
if (Number.isNaN(offset)) {
throw Error(`Invalid offset ${offset} specified`);

@@ -335,0 +335,0 @@ }

// TODO: Refactor further
export class ListProps {
scrollToIndex = $state(-1);
scrollToAlignment = $state('');
scrollOffset = $state(0);
itemCount = $state(0);
itemSize = $state(0);
estimatedItemSize = $state(0);
height = $state(400);
width = $state(400);
stickyIndices = $state([]);
scrollToIndex = $state(-1);
scrollToAlignment = $state("");
scrollOffset = $state(0);
itemCount = $state(0);
itemSize = $state(0);
estimatedItemSize = $state(0);
height = $state(400);
width = $state(400);
stickyIndices = $state([]);
// Default values
previousState = $state.raw({
scrollToIndex: -1,
scrollToAlignment: 'start',
scrollOffset: 0,
itemCount: 0,
itemSize: 0,
estimatedItemSize: 50,
height: 400,
width: 400,
stickyIndices: []
});
// Default values
previousState = $state.raw({
scrollToIndex: -1,
scrollToAlignment: "start",
scrollOffset: 0,
itemCount: 0,
itemSize: 0,
estimatedItemSize: 50,
height: 400,
width: 400,
stickyIndices: []
});
get hasScrollOffsetChanged() {
return this.previousState.scrollOffset !== this.scrollOffset;
}
get hasScrollOffsetChanged () {
return this.previousState.scrollOffset !== this.scrollOffset;
}
get haveScrollPropsChanged() {
return (
this.previousState.scrollToIndex !== this.scrollToIndex ||
this.previousState.scrollToAlignment !== this.scrollToAlignment
);
}
get haveScrollPropsChanged () {
return this.previousState.scrollToIndex !== this.scrollToIndex ||
this.previousState.scrollToAlignment !== this.scrollToAlignment;
}
get haveSizesChanged() {
return (
this.previousState.itemCount !== this.itemCount ||
this.previousState.itemSize !== this.itemSize ||
this.previousState.estimatedItemSize !== this.estimatedItemSize
);
}
get haveSizesChanged () {
return this.previousState.itemCount !== this.itemCount ||
this.previousState.itemSize !== this.itemSize ||
this.previousState.estimatedItemSize !== this.estimatedItemSize;
}
get hasScrollIndexChanged() {
return this.scrollToIndex > -1 && (this.haveScrollPropsChanged || this.haveSizesChanged);
}
get hasScrollIndexChanged () {
return this.scrollToIndex > -1 &&
(this.haveScrollPropsChanged || this.haveSizesChanged);
}
get haveDimsOrStickyIndicesChanged() {
return (
this.previousState.height !== this.height ||
this.previousState.width !== this.width ||
this.previousState.stickyIndices.toString() !== $state.snapshot(this.stickyIndices).toString()
);
}
get haveDimsOrStickyIndicesChanged () {
return this.previousState.height !== this.height ||
this.previousState.width !== this.width ||
this.previousState.stickyIndices.toString() !== $state.snapshot(this.stickyIndices).toString()
}
constructor(
scrollToIndex = -1,
scrollToAlignment = 'start',
scrollOffset = 0,
itemCount = 0,
itemSize = 0,
estimatedItemSize = 50,
height = 400,
width = 400,
stickyIndices = []
) {
this.scrollToIndex = scrollToIndex;
this.scrollToAlignment = scrollToAlignment;
this.scrollOffset = scrollOffset;
this.itemCount = itemCount;
this.itemSize = itemSize;
this.estimatedItemSize = estimatedItemSize;
this.height = height;
this.width = width;
this.stickyIndices = stickyIndices;
}
constructor (
scrollToIndex = -1,
scrollToAlignment = "start",
scrollOffset = 0,
itemCount = 0,
itemSize = 0,
estimatedItemSize = 50,
height = 400,
width = 400,
stickyIndices = []
) {
this.scrollToIndex = scrollToIndex;
this.scrollToAlignment = scrollToAlignment;
this.scrollOffset = scrollOffset;
this.itemCount = itemCount;
this.itemSize = itemSize;
this.estimatedItemSize = estimatedItemSize;
this.height = height;
this.width = width;
this.stickyIndices = stickyIndices;
}
listen(
scrollToIndex,
scrollToAlignment,
scrollOffset,
itemCount,
itemSize,
estimatedItemSize,
height,
width,
stickyIndices
) {
if (typeof scrollToIndex === 'number') this.scrollToIndex = scrollToIndex;
listen (
scrollToIndex,
scrollToAlignment,
scrollOffset,
itemCount,
itemSize,
estimatedItemSize,
height,
width,
stickyIndices
) {
if (typeof scrollToIndex === "number")
this.scrollToIndex = scrollToIndex;
if (typeof scrollToAlignment === 'string') this.scrollToAlignment = scrollToAlignment;
if (typeof scrollToAlignment === "string")
this.scrollToAlignment = scrollToAlignment;
if (typeof scrollOffset === 'number') this.scrollOffset = scrollOffset;
if (typeof scrollOffset === "number")
this.scrollOffset = scrollOffset;
if (typeof itemCount === 'number') this.itemCount = itemCount;
if (typeof itemCount === "number")
this.itemCount = itemCount;
if (typeof itemCount === 'number') this.itemCount = itemCount;
if (typeof itemCount === "number")
this.itemCount = itemCount;
if (typeof itemSize === 'number' || typeof itemSize === 'function' || Array.isArray(itemSize))
this.itemSize = itemSize;
if (typeof itemSize === "number" || typeof itemSize === "function" || Array.isArray(itemSize))
this.itemSize = itemSize;
if (typeof estimatedItemSize === 'number')
this.estimatedItemSize = estimatedItemSize || this.itemSize || 50;
if (typeof estimatedItemSize === "number")
this.estimatedItemSize = estimatedItemSize || this.itemSize || 50;
if (typeof height === 'number') this.height = height;
if (typeof height === "number")
this.height = height;
if (typeof width === 'number') this.width = width;
if (typeof width === "number")
this.width = width;
if (Array.isArray(stickyIndices)) this.stickyIndices = stickyIndices;
}
if (Array.isArray(stickyIndices))
this.stickyIndices = stickyIndices;
}
update() {
this.#updateRenderedStateSnapshot();
}
update () {
this.#updateRenderedStateSnapshot();
}
#updateRenderedStateSnapshot () {
this.previousState = {
scrollToIndex: $state.snapshot(this.scrollToIndex),
scrollToAlignment: $state.snapshot(this.scrollToAlignment),
scrollOffset: $state.snapshot(this.scrollOffset),
itemCount: $state.snapshot(this.itemCount),
itemSize: $state.snapshot(this.itemSize),
estimatedItemSize: $state.snapshot(this.estimatedItemSize)
};
}
#updateRenderedStateSnapshot() {
this.previousState = {
scrollToIndex: $state.snapshot(this.scrollToIndex),
scrollToAlignment: $state.snapshot(this.scrollToAlignment),
scrollOffset: $state.snapshot(this.scrollOffset),
itemCount: $state.snapshot(this.itemCount),
itemSize: $state.snapshot(this.itemSize),
estimatedItemSize: $state.snapshot(this.estimatedItemSize)
};
}
}

@@ -1,46 +0,47 @@

import { SCROLL_CHANGE_REASON } from "../constants.js";
import { SCROLL_CHANGE_REASON } from '../constants.js';
// TODO: Refactor further
export class ListState {
offset = $state(0);
scrollChangeReason = $state(SCROLL_CHANGE_REASON.REQUESTED);
offset = $state(0);
scrollChangeReason = $state(SCROLL_CHANGE_REASON.REQUESTED);
previousState = $state.raw({
offset: 0,
scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED
});
previousState = $state.raw({
offset: 0,
scrollChangeReason: SCROLL_CHANGE_REASON.REQUESTED
});
get doRefresh() {
return (
this.offset !== this.previousState.offset ||
this.scrollChangeReason !== this.previousState.scrollChangeReason
);
}
get doRefresh () {
return this.offset !== this.previousState.offset ||
this.scrollChangeReason !== this.previousState.scrollChangeReason;
};
get doScrollToOffset() {
return (
this.offset !== this.previousState.offset &&
this.scrollChangeReason === SCROLL_CHANGE_REASON.REQUESTED
);
}
get doScrollToOffset () {
return this.offset !== this.previousState.offset &&
this.scrollChangeReason === SCROLL_CHANGE_REASON.REQUESTED;
};
constructor(offset = 0) {
this.offset = offset;
}
constructor (offset = 0) {
this.offset = offset;
}
listen(offset, scrollChangeReason) {
if (typeof offset === 'number') this.offset = offset;
listen (offset, scrollChangeReason) {
if (typeof offset === "number")
this.offset = offset;
if (typeof scrollChangeReason === 'number') this.scrollChangeReason = scrollChangeReason;
}
if (typeof scrollChangeReason === "number")
this.scrollChangeReason = scrollChangeReason;
}
update() {
this.#updateRenderedStateSnapshot();
}
update () {
this.#updateRenderedStateSnapshot();
}
#updateRenderedStateSnapshot () {
this.previousState = {
offset: $state.snapshot(this.offset),
scrollChangeReason: $state.snapshot(this.scrollChangeReason)
};
}
#updateRenderedStateSnapshot() {
this.previousState = {
offset: $state.snapshot(this.offset),
scrollChangeReason: $state.snapshot(this.scrollChangeReason)
};
}
}
MIT License
===========
Copyright (c) 2024 Jonas Geiler
Copyright (c) 2025 Jonas Geiler

@@ -6,0 +6,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "svelte-tiny-virtual-list",
"version": "4.0.0-rc.0",
"version": "4.0.0-rc.1",
"description": "A tiny but mighty list virtualization component for svelte, with zero dependencies 💪",
"homepage": "https://github.com/jonasgeiler/svelte-tiny-virtual-list#readme",
"homepage": "https://svelte-tiny-virtual-list.jonasgeiler.com",
"bugs": "https://github.com/jonasgeiler/svelte-tiny-virtual-list/issues",
"funding": "https://github.com/sponsors/jonasgeiler",
"license": "MIT",

@@ -13,2 +14,5 @@ "author": "Jonas Geiler <npm@jonasgeiler.com> (https://jonasgeiler.com)",

"repository": "github:jonasgeiler/svelte-tiny-virtual-list",
"engines": {
"node": ">=20.17.0"
},
"peerDependencies": {

@@ -18,2 +22,5 @@ "svelte": "^5.0.0"

"devDependencies": {
"@biomejs/biome": "^2.0.6",
"@eslint/compat": "^1.2.5",
"@eslint/js": "^9.18.0",
"@playwright/test": "^1.53.0",

@@ -24,3 +31,3 @@ "@sveltejs/adapter-static": "^3.0.8",

"@sveltejs/vite-plugin-svelte": "^5.1.0",
"@types/eslint": "^9.6.1",
"@vitest/browser": "^3.2.3",
"beercss": "3.6.13",

@@ -30,5 +37,8 @@ "eslint": "^9.29.0",

"eslint-plugin-svelte": "^3.9.2",
"globals": "^16.0.0",
"lint-staged": "^16.1.2",
"marked": "^15.0.12",
"marked-base-url": "1.1.6",
"marked-gfm-heading-id": "4.1.1",
"playwright": "^1.53.0",
"prettier": "^3.5.3",

@@ -38,8 +48,7 @@ "prettier-plugin-svelte": "^3.4.0",

"svelte": "^5.34.5",
"svelte-check": "^4.2.1",
"svelte-infinite-loading": "1.4.0",
"tslib": "2.8.1",
"typescript": "^5.8.3",
"typescript": "^5.3.2",
"vite": "^6.3.5",
"vitest": "^3.2.4"
"vitest": "^3.2.4",
"vitest-browser-svelte": "^0.1.0"
},

@@ -51,2 +60,5 @@ "files": [

],
"sideEffects": [
"**/*.css"
],
"type": "module",

@@ -61,5 +73,2 @@ "svelte": "./dist/index.js",

},
"engines": {
"node": ">=18.18.0"
},
"keywords": [

@@ -78,13 +87,18 @@ "svelte",

"dev": "vite dev",
"build": "vite build && pnpm run package",
"start": "pnpm run dev",
"build": "vite build && pnpm run prepack",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
"test": "pnpm run test:integration && pnpm run test:unit",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"test:integration": "playwright test",
"test:unit": "vitest"
"check": "pnpm run '/^check:.*/'",
"check:biome": "biome check",
"check:eslint": "eslint .",
"check:prettier": "prettier --check .",
"check:vitest": "vitest run",
"check:playwright": "playwright test",
"fix": "pnpm run fix:biome && pnpm run fix:eslint && pnpm run fix:prettier",
"fix:biome": "biome check --fix",
"fix:eslint": "eslint --fix .",
"fix:prettier": "prettier --write .",
"fix-unsafe": "pnpm run fix-unsafe:biome",
"fix-unsafe:biome": "biome check --fix --unsafe"
}
}

@@ -1,2 +0,2 @@

<p align="center"><img src="https://raw.githubusercontent.com/jonasgeiler/svelte-tiny-virtual-list/main/static/logo.svg" alt="Logo" width="225"></p>
<p align="center"><img src="./static/logo.svg" alt="Logo" width="225"></p>
<h2 align="center">svelte-tiny-virtual-list</h2>

@@ -14,3 +14,3 @@ <p align="center">A tiny but mighty list virtualization library, with zero dependencies &#128170;</p>

<a href="#usage">Usage</a> •
<a href="#examples--demo-outdated">Examples</a> •
<a href="#examples--demo">Examples</a> •
<a href="#license">License</a>

@@ -111,19 +111,19 @@ </p>

| Property | Type | Required? | Description |
| ----------------- | ------------------------------------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| width | `number \| string` | ✓ | Width of List. This property will determine the number of rendered items when scrollDirection is `'horizontal'`. |
| height | `number \| string` | ✓ | Height of List. This property will determine the number of rendered items when scrollDirection is `'vertical'`. |
| itemCount | `number` | ✓ | The number of items you want to render |
| itemSize | `number \| number[] \| (index: number) => number` | ✓ | Either a fixed height/width (depending on the scrollDirection), an array containing the heights of all the items in your list, or a function that returns the height of an item given its index: `(index: number): number` |
| scrollDirection | `string` | | Whether the list should scroll vertically or horizontally. One of `'vertical'` (default) or `'horizontal'`. |
| scrollOffset | `number` | | Can be used to control the scroll offset; Also useful for setting an initial scroll offset |
| scrollToIndex | `number` | | Item index to scroll to (by forcefully scrolling if necessary) |
| scrollToAlignment | `string` | | Used in combination with `scrollToIndex`, this prop controls the alignment of the scrolled to item. One of: `'start'`, `'center'`, `'end'` or `'auto'`. Use `'start'` to always align items to the top of the container and `'end'` to align them bottom. Use `'center`' to align them in the middle of the container. `'auto'` scrolls the least amount possible to ensure that the specified `scrollToIndex` item is fully visible. |
| scrollToBehaviour | `string` | | Used in combination with `scrollToIndex`, this prop controls the behaviour of the scrolling. One of: `'auto'`, `'smooth'` or `'instant'` (default). |
| stickyIndices | `number[]` | | An array of indexes (eg. `[0, 10, 25, 30]`) to make certain items in the list sticky (`position: sticky`) |
| overscanCount | `number` | | Number of extra buffer items to render above/below the visible items. Tweaking this can help reduce scroll flickering on certain browsers/devices. |
| estimatedItemSize | `number` | | Used to estimate the total size of the list before all of its items have actually been measured. The estimated total height is progressively adjusted as items are rendered. |
| getKey | `(index: number) => any` | | Function that returns the key of an item in the list, which is used to uniquely identify an item. This is useful for dynamic data coming from a database or similar. By default, it's using the item's index.
| onAfterScroll | `({ event: ScrollEvent, offset: number }) => any` | | Function that fires after handling the scroll event. Props: `event: ScrollEvent` - The original scroll event, `offset: number` - Either the value of `wrapper.scrollTop` or `wrapper.scrollLeft`
| onListItemsUpdate | `({ start: number, end: number }) => any` | | Function that fires when the visible items are updated. Props: `start: number` - Index of the first visible item, `end: number` - Index of the last visible item. |
| Property | Type | Required? | Description |
| ----------------- | -------------------------------------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| width | `number \| string` | ✓ | Width of List. This property will determine the number of rendered items when scrollDirection is `'horizontal'`. |
| height | `number \| string` | ✓ | Height of List. This property will determine the number of rendered items when scrollDirection is `'vertical'`. |
| itemCount | `number` | ✓ | The number of items you want to render |
| itemSize | `number \| number[] \| (index: number) => number` | ✓ | Either a fixed height/width (depending on the scrollDirection), an array containing the heights of all the items in your list, or a function that returns the height of an item given its index: `(index: number): number` |
| scrollDirection | `string` | | Whether the list should scroll vertically or horizontally. One of `'vertical'` (default) or `'horizontal'`. |
| scrollOffset | `number` | | Can be used to control the scroll offset; Also useful for setting an initial scroll offset |
| scrollToIndex | `number` | | Item index to scroll to (by forcefully scrolling if necessary) |
| scrollToAlignment | `string` | | Used in combination with `scrollToIndex`, this prop controls the alignment of the scrolled to item. One of: `'start'`, `'center'`, `'end'` or `'auto'`. Use `'start'` to always align items to the top of the container and `'end'` to align them bottom. Use `'center`' to align them in the middle of the container. `'auto'` scrolls the least amount possible to ensure that the specified `scrollToIndex` item is fully visible. |
| scrollToBehaviour | `string` | | Used in combination with `scrollToIndex`, this prop controls the behaviour of the scrolling. One of: `'auto'`, `'smooth'` or `'instant'` (default). |
| stickyIndices | `number[]` | | An array of indexes (eg. `[0, 10, 25, 30]`) to make certain items in the list sticky (`position: sticky`) |
| overscanCount | `number` | | Number of extra buffer items to render above/below the visible items. Tweaking this can help reduce scroll flickering on certain browsers/devices. |
| estimatedItemSize | `number` | | Used to estimate the total size of the list before all of its items have actually been measured. The estimated total height is progressively adjusted as items are rendered. |
| getKey | `(index: number) => any` | | Function that returns the key of an item in the list, which is used to uniquely identify an item. This is useful for dynamic data coming from a database or similar. By default, it's using the item's index. |
| onAfterScroll | `({ event: ScrollEvent, offset: number }) => void` | | Function that fires after handling the scroll event. Props: `event: ScrollEvent` - The original scroll event, `offset: number` - Either the value of `wrapper.scrollTop` or `wrapper.scrollLeft` |
| onListItemsUpdate | `({ start: number, end: number }) => void` | | Function that fires when the visible items are updated. Props: `start: number` - Index of the first visible item, `end: number` - Index of the last visible item. |

@@ -213,12 +213,12 @@ ### Snippets

## Examples / Demo (OUTDATED)
## Examples / Demo
- **Basic setup**
- [Elements of equal height](https://svelte.dev/playground/e3811b44f311461dbbc7c2df830cde68)
- [Variable heights](https://svelte.dev/playground/93795c812f8d4541b6b942535b2ed855)
- [Horizontal list](https://svelte.dev/playground/4cd8acdfc96843b68265a19451b1bf3d)
- [Elements of equal height](https://svelte.dev/playground/59a98b2a0b53421e8fce3df8e66f5e3b)
- [Variable heights](https://svelte.dev/playground/51952d1becb345c18154a0a61106617f)
- [Horizontal list](https://svelte.dev/playground/d1ca5e2462cf46d5966c98c2cbf80be5)
- **Controlled props**
- [Scroll to index](https://svelte.dev/playground/bdf5ceb63f6e45f7bb14b90dbd2c11d9)
- [Controlled scroll offset](https://svelte.dev/playground/68576a3919c44033a74416d4bc4fde7e)
- [Hacker News using svelte-infinite-loading](https://svelte.dev/playground/2239cc4c861c41d18abbc858248f5a0d)
- [Scroll to index](https://svelte.dev/playground/8610d15e77d34667a65d981b9327a7ad)
- [Controlled scroll offset](https://svelte.dev/playground/39452c8fe30b4f618760a1198f6b9769)
- [Hacker News using svelte-infinite-loading](https://svelte.dev/playground/6719ab725cfd440b9bd3cfa9b85d9d3a)

@@ -225,0 +225,0 @@ ## License

Sorry, the diff of this file is not supported yet