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

Added TS typings to VirtualScroll.svelte #28

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 4 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
# svelte-virtual-scroll-list changelog

## 1.3.0
## 1.0.1

- Expose `index` from VirtualScroll component
- Fixed issue where data changes wouldn't refresh the item contents

## 1.2.0
## 1.0.0

- Move example to SvelteKit
- Package distribution by SvelteKit too
- Add classes on VS wrappers
- Add example for horizontal scroll
- Fix pageMode with SSR
- Support Svelte 4
- Forked and heavily modified from original package svelte-virtual-scroll-list
35 changes: 14 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@
# svelte-virtual-scroll-list

[![npm](https://img.shields.io/npm/v/svelte-virtual-scroll-list?style=for-the-badge)](https://npmjs.com/package/svelte-virtual-scroll-list/)
[![npm](https://img.shields.io/npm/v/@josesan9/svelte-virtual-scroll-list?style=for-the-badge)](https://npmjs.com/package/@josesan9/svelte-virtual-scroll-list/)

Svelte implementation of vue library [vue-virtual-scroll-list](https://github.com/tangbc/vue-virtual-scroll-list)
fork of same library by v1ack [svelte-virtual-scroll-list](https://github.com/v1ack/svelte-virtual-scroll-list)

Virtualized scrolling for big lists

---
**Support dynamic both-directional lists** (see example)

---

Online demo: [https://v1ack.github.io/svelte-virtual-scroll-list/](https://v1ack.github.io/svelte-virtual-scroll-list/)

[Simple example in Svelte REPL](https://ru.svelte.dev/repl/eae82aab17b04420885851d58de50a2e?version=3.38.2)
Virtualized scrolling for big lists. For now this does not support bi-directionality (unlike v1ach's implementation)

# Getting started

## Installing from npm
`npm i josesan9/svelte-virtual-scroll-list -D`

`npm i svelte-virtual-scroll-list -D`

or

`yarn add svelte-virtual-scroll-list -D`

## Using
## Usage

```html

Expand Down Expand Up @@ -61,7 +47,7 @@ More examples available in `example` folder
|---------------------------|----------------------------|---------------------|----------------------------------|
| handle dynamic size data | + | + | - |
| scroll methods (to index) | + | - | + |
| infinity scrolling | two-directional | - | one-directional with another lib |
| infinity scrolling | + | - | one-directional with another lib |
| initial scroll position | + | - | + |
| sticky items | - | - | + |
| top/bottom slots | + | - | + |
Expand Down Expand Up @@ -125,7 +111,13 @@ Access to methods by component binding

## Additional

### Get index of current rendering items
### Params passed down to each virtual list item

| param | description |
|------------|------------------------------------------------------|
| data | data item |
| index | index of item (in relation to full list) |
| localIndex | index of item (in relation to rendered items only) |

```html

Expand All @@ -134,6 +126,7 @@ Access to methods by component binding
key="id"
let:data
let:index
let:localIndex
>
<div>
{data.text} {index}
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"name": "svelte-virtual-scroll-list",
"name": "@josesan9/svelte-virtual-scroll-list",
"description": "Svelte lib for virtualizing lists",
"author": {
"name": "v1ack",
"url": "https://github.com/v1ack"
"name": "josesan9",
"url": "https://github.com/josesan9"
},
"keywords": [
"svelte",
"virtual",
"virtual-list",
"virtual-scroll"
],
"version": "1.3.0",
"version": "1.0.1",
"repository": {
"type": "git",
"url": "git+https://github.com/v1ack/svelte-virtual-scroll-list.git"
"url": "git+https://github.com/josesan9/svelte-virtual-scroll-list.git"
},
"scripts": {
"dev": "vite dev",
Expand Down
10 changes: 5 additions & 5 deletions src/lib/Item.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script>
<script lang="ts" generics="T">
import {afterUpdate, createEventDispatcher, onDestroy, onMount} from "svelte"

export let horizontal = false
export let uniqueKey
export let uniqueKey: any
export let type = "item"

let resizeObserver
let itemDiv
let previousSize
let resizeObserver: ResizeObserver | null
let itemDiv: HTMLDivElement
let previousSize: number

const dispatch = createEventDispatcher()
const shapeKey = horizontal ? "offsetWidth" : "offsetHeight"
Expand Down
100 changes: 44 additions & 56 deletions src/lib/VirtualScroll.svelte
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
<script>
import Virtual, {isBrowser} from "./virtual"
<script lang="ts" generics="T">
import { isBrowser, Virtual } from "./virtual.js"
import Item from "./Item.svelte"
import {createEventDispatcher, onDestroy, onMount} from "svelte"

/**
* Unique key for getting data from `data`
* @type {string}
*/
export let key = "id"
export let key: keyof T | typeof keyFn

let keyFn: (item: T, index: number) => any = key instanceof Function ? key : (item: T) => item[key]
/**
* Source for list
* @type {Array<any>}
*/
export let data
export let data: T[]
/**
* Count of rendered items
* Count of items rendered outside of view (for each direction)
* @type {number}
*/
export let keeps = 30
export let overflow = 5
/**
* Estimate size of each item, needs for smooth scrollbar
* @type {number}
*/
export let estimateSize = 50
export let estimateSize: number | ((item: T) => number) = 50
/**
* Scroll direction
* @type {boolean}
Expand All @@ -42,36 +44,35 @@
*/
export let pageMode = false
/**
* The threshold to emit `top` event, attention to multiple calls.
* The threshold to emit `top` event in px, attention to multiple calls.
* @type {number}
*/
export let topThreshold = 0
/**
* The threshold to emit `bottom` event, attention to multiple calls.
* The threshold to emit `bottom` event in px, attention to multiple calls.
* @type {number}
*/
export let bottomThreshold = 0

let displayItems = []
let paddingStyle
let directionKey = isHorizontal ? "scrollLeft" : "scrollTop"
let range = null
let displayItems: T[] = []
let paddingStyle: string
let directionKey = isHorizontal ? "scrollLeft" as const : "scrollTop" as const
let virtual = new Virtual({
slotHeaderSize: 0,
slotFooterSize: 0,
keeps: keeps,
estimateSize: estimateSize,
buffer: Math.round(keeps / 3), // recommend for a third of keeps
uniqueIds: getUniqueIdFromDataSources(),
}, onRangeChanged)
let root
let shepherd
overflow: overflow,
data: data,
}, onRangeChanged, keyFn, estimateSize)
let range = virtual.getRange()
let root: HTMLDivElement
let shepherd: HTMLDivElement
let resizeObserver = new ResizeObserver(() => onScroll())
const dispatch = createEventDispatcher()

/**
* @type {(id: number) => number}
*/
export function getSize(id) {
export function getSize(id: any) {
return virtual.sizes.get(id)
}

Expand Down Expand Up @@ -125,15 +126,15 @@
if (root && isBrowser()) {
const rect = root.getBoundingClientRect()
const {defaultView} = root.ownerDocument
const offsetFront = isHorizontal ? (rect.left + defaultView.pageXOffset) : (rect.top + defaultView.pageYOffset)
const offsetFront = isHorizontal ? (rect.left + defaultView!.pageXOffset) : (rect.top + defaultView!.pageYOffset)
virtual.updateParam("slotHeaderSize", offsetFront)
}
}

/**
* @type {(offset: number) => void}
*/
export function scrollToOffset(offset) {
export function scrollToOffset(offset: number) {
if (!isBrowser()) return
if (pageMode) {
document.body[directionKey] = offset
Expand All @@ -146,7 +147,7 @@
/**
* @type {(index: number) => void}
*/
export function scrollToIndex(index) {
export function scrollToIndex(index: number) {
if (index >= data.length - 1) {
scrollToBottom()
} else {
Expand Down Expand Up @@ -175,6 +176,7 @@
}

onMount(() => {
onScroll()
if (start) {
scrollToIndex(start)
} else if (offset) {
Expand All @@ -188,20 +190,17 @@
passive: false,
})
}
resizeObserver.observe(root);
})

onDestroy(() => {
virtual.destroy()
resizeObserver.disconnect();
if (pageMode && isBrowser()) {
document.removeEventListener("scroll", onScroll)
}
})

function getUniqueIdFromDataSources() {
return data.map((dataSource) => dataSource[key])
}

function onItemResized(event) {
function onItemResized(event: CustomEvent<any>) {
const {id, size, type} = event.detail
if (type === "item")
virtual.saveSize(id, size)
Expand All @@ -215,50 +214,39 @@
}
}

function onRangeChanged(range_) {
function onRangeChanged(range_: any) {
range = range_
paddingStyle = paddingStyle = isHorizontal ? `0px ${range.padBehind}px 0px ${range.padFront}px` : `${range.padFront}px 0px ${range.padBehind}px`
displayItems = data.slice(range.start, range.end + 1)
}

function onScroll(event) {
function onScroll(event?: Event) {
const offset = getOffset()
const clientSize = getClientSize()
const scrollSize = getScrollSize()

// iOS scroll-spring-back behavior will make direction mistake
if (offset < 0 || (offset + clientSize > scrollSize) || !scrollSize) {
return
}

virtual.handleScroll(offset)
emitEvent(offset, clientSize, scrollSize, event)
virtual.handleScroll(offset, clientSize)
if (event)
emitEvent(offset, clientSize, scrollSize, event)
}

function emitEvent(offset, clientSize, scrollSize, event) {
dispatch("scroll", {event, range: virtual.getRange()})
function emitEvent(offset: number, clientSize: number, scrollSize: number, event: Event) {
const range = virtual.getRange()
dispatch("scroll", {event, range: range})

if (virtual.isFront() && !!data.length && (offset - topThreshold <= 0)) {
if (offset <= topThreshold) {
dispatch("top")
} else if (virtual.isBehind() && (offset + clientSize + bottomThreshold >= scrollSize)) {
} else if ((scrollSize - offset) - clientSize >= bottomThreshold) {
dispatch("bottom")
}
}

$: scrollToOffset(offset)
$: scrollToIndex(start)
$: handleKeepsChange(keeps)

function handleKeepsChange(keeps) {
virtual.updateParam("keeps", keeps)
virtual.handleSlotSizeChange()
}

$: handleDataSourcesChange(data)

async function handleDataSourcesChange(data) {
virtual.updateParam("uniqueIds", getUniqueIdFromDataSources())
virtual.handleDataSourcesChange()
async function handleDataSourcesChange(data: T[]) {
virtual.updateParam("data", data)
}
</script>

Expand All @@ -269,13 +257,13 @@
</Item>
{/if}
<div style="padding: {paddingStyle}" class="virtual-scroll-wrapper">
{#each displayItems as dataItem, dataIndex (dataItem[key])}
{#each displayItems as dataItem, dataIndex (keyFn(dataItem, dataIndex + range.start))}
<Item
on:resize={onItemResized}
uniqueKey={dataItem[key]}
uniqueKey={keyFn(dataItem, dataIndex + range.start)}
horizontal={isHorizontal}
type="item">
<slot data={dataItem} index={dataIndex} />
<slot data={dataItem} index={dataIndex + range.start} localIndex={dataIndex} />
</Item>
{/each}
</div>
Expand Down
File renamed without changes.
Loading