From 431f75f6e19718543921164a92d72d9b34cc9dfb Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Wed, 24 Jan 2024 03:00:35 -0800 Subject: [PATCH 01/17] Make height prop optional --- src/SizeAndPositionManager.js | 4 +-- src/VirtualList.svelte | 64 +++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/SizeAndPositionManager.js b/src/SizeAndPositionManager.js index fc70b5b..2fe939c 100644 --- a/src/SizeAndPositionManager.js +++ b/src/SizeAndPositionManager.js @@ -283,7 +283,7 @@ export default class SizeAndPositionManager { return {}; } - const maxOffset = offset + containerSize; + const maxOffset = Math.max(0, offset || 0) + containerSize; let start = this.findNearestItem(offset); if (start === undefined) { @@ -299,7 +299,7 @@ export default class SizeAndPositionManager { stop++; offset += this.getSizeAndPositionForIndex(stop).size; } - + if (overscanCount) { start = Math.max(0, start - overscanCount); stop = Math.min(stop + overscanCount, this.itemCount - 1); diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 3c5cbe0..60670dc 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -60,6 +60,7 @@ let mounted = false; let wrapper; + let scrollWrapper; let items = []; let state = { @@ -101,7 +102,13 @@ onMount(() => { mounted = true; - wrapper.addEventListener('scroll', handleScroll, thirdEventArg); + if ((scrollDirection === DIRECTION.VERTICAL && height) || (scrollDirection === DIRECTION.HORIZONTAL && width)) { + scrollWrapper = wrapper; + } else { + scrollWrapper = window; + } + + scrollWrapper.addEventListener('scroll', handleScroll, thirdEventArg); if (scrollOffset != null) { scrollTo(scrollOffset); @@ -111,7 +118,9 @@ }); onDestroy(() => { - if (mounted) wrapper.removeEventListener('scroll', handleScroll); + if (mounted) { + scrollWrapper.removeEventListener('scroll', handleScroll); + } }); @@ -187,20 +196,22 @@ function refresh() { const { offset } = state; + + const containerSize = getContainerSize(); const { start, stop } = sizeAndPositionManager.getVisibleRange({ - containerSize: scrollDirection === DIRECTION.VERTICAL ? height : width, + containerSize, offset, overscanCount, }); - + let updatedItems = []; const totalSize = sizeAndPositionManager.getTotalSize(); if (scrollDirection === DIRECTION.VERTICAL) { - wrapperStyle = `height:${height}px;width:${width};`; + wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width}; overflow: ${height ? 'auto' : 'hidden'}`; innerStyle = `flex-direction:column;height:${totalSize}px;`; } else { - wrapperStyle = `height:${height};width:${width}px`; + wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width}px; overflow: ${width ? 'auto' : 'hidden'}`; innerStyle = `min-height:100%;width:${totalSize}px;`; } @@ -238,13 +249,17 @@ function scrollTo(value) { - if ('scroll' in wrapper) { - wrapper.scroll({ + if (scrollDirection === DIRECTION.VERTICAL && !height) { + value += wrapper.offsetTop; + } + + if ('scroll' in scrollWrapper) { + scrollWrapper.scroll({ [SCROLL_PROP[scrollDirection]]: value, behavior: scrollToBehaviour, }); } else { - wrapper[SCROLL_PROP_LEGACY[scrollDirection]] = value; + scrollWrapper[SCROLL_PROP_LEGACY[scrollDirection]] = value; } } @@ -259,9 +274,10 @@ index = 0; } + const containerSize = getContainerSize(); return sizeAndPositionManager.getUpdatedOffsetForIndex({ align, - containerSize: scrollDirection === DIRECTION.VERTICAL ? height : width, + containerSize, currentOffset: state.offset || 0, targetIndex: index, }); @@ -269,9 +285,7 @@ function handleScroll(event) { const offset = getWrapperOffset(); - - if (offset < 0 || state.offset === offset || event.target !== wrapper) return; - + if (state.offset === offset || (event.target !== scrollWrapper && event.target !== document)) return; state = { offset, scrollChangeReason: SCROLL_CHANGE_REASON.OBSERVED, @@ -283,8 +297,28 @@ }); } + function getContainerSize() { + let visibleHeight = 0; + if (wrapper) { + const rect = wrapper.getBoundingClientRect(); + visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0); + } + + const containerSize = scrollDirection === DIRECTION.VERTICAL ? (height || visibleHeight) : width; + + return containerSize; + } + function getWrapperOffset() { - return wrapper[SCROLL_PROP_LEGACY[scrollDirection]]; + if ('scroll' in scrollWrapper) { + if (scrollWrapper === window) { + return document.documentElement.scrollTop - wrapper.offsetTop; + } else { + return scrollWrapper.scrollTop; + } + } else { + return scrollWrapper[SCROLL_PROP_LEGACY[scrollDirection]]; + } } function getEstimatedItemSize() { @@ -297,7 +331,7 @@ function getStyle(index, sticky) { if (styleCache[index]) return styleCache[index]; - + const { size, offset } = sizeAndPositionManager.getSizeAndPositionForIndex(index); let style; From 50b4c662ecfe5c9545547f84cf2ad11269a7abb1 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Thu, 25 Jan 2024 08:23:38 -0800 Subject: [PATCH 02/17] Add items prop, make itemsCount optional --- src/VirtualList.svelte | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 60670dc..7b510dc 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -36,7 +36,8 @@ export let height; export let width = '100%'; - export let itemCount; + export let items; + export let itemCount = items && items.length > 0 ? items.length : undefined; export let itemSize; export let estimatedItemSize = null; export let stickyIndices = null; @@ -61,7 +62,7 @@ let mounted = false; let wrapper; let scrollWrapper; - let items = []; + let visibleItems = []; let state = { offset: scrollOffset || (scrollToIndex != null && items.length && getOffsetForIndex(scrollToIndex)) || 0, @@ -244,7 +245,7 @@ }); } - items = updatedItems; + visibleItems = updatedItems; } @@ -362,8 +363,12 @@
- {#each items as item (getKey ? getKey(item.index) : item.index)} - + {#each visibleItems as item (getKey ? getKey(item.index) : item.index)} + {#if items && items.length > 0} + + {:else} + + {/if} {/each}
From 4d1dd48f21ac10522bfe87b8dd6db8dbde585ce6 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Thu, 25 Jan 2024 08:51:00 -0800 Subject: [PATCH 03/17] Update Readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aff1e2a..860648e 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,8 @@ Also works pretty well with [`svelte-infinite-loading`](https://github.com/Skayo | :---------------- | :------------------------------------------------ | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 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 | +| items | `any[]` | | The items you want to render | +| 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 | From fbe8add099f5f092a20358cdc6257b2e8c17a0a8 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Thu, 25 Jan 2024 19:05:09 -0800 Subject: [PATCH 04/17] Make items prop type generic --- types/index.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 2874056..22ec047 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,6 +9,8 @@ export type Direction = "horizontal" | "vertical"; export type ItemSizeGetter = (index: number) => number; export type ItemSize = number | number[] | ItemSizeGetter; +type T = $$Generic; + /** * VirtualList props */ @@ -25,10 +27,15 @@ export interface VirtualListProps { */ height: number | string; + /** + * The items you want to render + */ + items?: Array; + /** * The number of items you want to render */ - itemCount: number; + itemCount?: number; /** * Either a fixed height/width (depending on the scrollDirection), From a91194af61a107811861d0550cbfd831d6baf157 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Fri, 26 Jan 2024 06:10:30 -0800 Subject: [PATCH 05/17] Add "item" attribute to item slot --- types/index.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/types/index.d.ts b/types/index.d.ts index 22ec047..b1e7f4f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -115,6 +115,11 @@ export interface VirtualListSlots { * Slot for each item */ item: { + /** + * Item + */ + item: T, + /** * Item index */ From df0b425da9f37cef22cf03d0db6236986127e454 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Fri, 26 Jan 2024 07:15:42 -0800 Subject: [PATCH 06/17] Update items prop type --- types/index.d.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index b1e7f4f..8275cb9 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -9,8 +9,6 @@ export type Direction = "horizontal" | "vertical"; export type ItemSizeGetter = (index: number) => number; export type ItemSize = number | number[] | ItemSizeGetter; -type T = $$Generic; - /** * VirtualList props */ @@ -25,12 +23,12 @@ export interface VirtualListProps { /** * Height of List. This property will determine the number of rendered items when scrollDirection is `'vertical'`. */ - height: number | string; + height?: number | string; /** * The items you want to render */ - items?: Array; + items?: any[]; /** * The number of items you want to render From 375d2c886d4b055ce567de9b630da10ad695f65e Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Fri, 26 Jan 2024 07:21:32 -0800 Subject: [PATCH 07/17] Update "item" attribute type --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 8275cb9..57353f0 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -116,7 +116,7 @@ export interface VirtualListSlots { /** * Item */ - item: T, + item: any, /** * Item index From aaaa282239fe96f4c73615b5c90e8dfbfe7b0c27 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Wed, 31 Jan 2024 00:53:51 -0800 Subject: [PATCH 08/17] Update component types --- package.json | 9 +- src/SizeAndPositionManager.js | 4 +- src/VirtualList.svelte | 90 ++++++---------- src/index.js | 1 - types/index.d.ts | 196 ---------------------------------- 5 files changed, 39 insertions(+), 261 deletions(-) delete mode 100644 src/index.js delete mode 100644 types/index.d.ts diff --git a/package.json b/package.json index dc52054..942d1b3 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,8 @@ "name": "svelte-tiny-virtual-list", "version": "2.0.5", "description": "A tiny but mighty list virtualization component for svelte, with zero dependencies 💪", - "svelte": "src/index.js", - "main": "dist/svelte-tiny-virtual-list.js", - "module": "dist/svelte-tiny-virtual-list.mjs", - "types": "types/index.d.ts", + "svelte": "src/VirtualList.svelte", + "main": "src/VirtualList.svelte", "scripts": { "build": "rollup -c", "lint": "eslint src/** test/**", @@ -29,8 +27,7 @@ }, "files": [ "src", - "dist", - "types" + "dist" ], "keywords": [ "svelte", diff --git a/src/SizeAndPositionManager.js b/src/SizeAndPositionManager.js index 2fe939c..acd1c1f 100644 --- a/src/SizeAndPositionManager.js +++ b/src/SizeAndPositionManager.js @@ -240,7 +240,7 @@ export default class SizeAndPositionManager { * @param {number | undefined} targetIndex * @return {number} Offset to use to ensure the specified item is visible */ - getUpdatedOffsetForIndex({ align = ALIGNMENT.START, containerSize, currentOffset, targetIndex }) { + getUpdatedOffsetForIndex(align = ALIGNMENT.START, containerSize, currentOffset, targetIndex) { if (containerSize <= 0) { return 0; } @@ -276,7 +276,7 @@ export default class SizeAndPositionManager { * @param {number} overscanCount * @return {{stop: number|undefined, start: number|undefined}} */ - getVisibleRange({ containerSize = 0, offset, overscanCount }) { + getVisibleRange(containerSize = 0, offset, overscanCount) { const totalSize = this.getTotalSize(); if (totalSize === 0) { diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 7b510dc..6e8a48f 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -1,29 +1,4 @@ - - - @@ -346,7 +379,10 @@
{#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - + + {#if expandItems[item.index]} + + {/if} {/each}
From 8ffb17d448a8f19e3dcb5eb01685c2be4839eb58 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Mon, 5 Feb 2024 05:26:22 -0800 Subject: [PATCH 10/17] Add mode(div|table) prop --- src/VirtualList.svelte | 35 +++++++++++++++++++++++++++-------- src/constants.js | 5 +++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index e59fecc..9b4aafc 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -6,11 +6,13 @@ SCROLL_CHANGE_REASON, SCROLL_PROP, SCROLL_PROP_LEGACY, + WRAPPER_MODE, } from './constants'; type Alignment = "auto" | "start" | "center" | "end"; type ScrollBehaviour = "auto" | "smooth" | "instant"; type Direction = "horizontal" | "vertical"; + type WrapperMode = "div" | "table"; type ItemSizeGetter = (index: number) => number; type ItemSize = number | number[] | ItemSizeGetter; type T = $$Generic; @@ -36,6 +38,8 @@ export let overscanCount: number = 3; + export let mode: WrapperMode = 'div'; + const dispatchEvent = createEventDispatcher(); const sizeAndPositionManager = new SizeAndPositionManager({ @@ -377,14 +381,29 @@
-
- {#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - - {#if expandItems[item.index]} - - {/if} - {/each} -
+ {#if mode === WRAPPER_MODE.DIV} +
+ {#each visibleItems as item (getKey ? getKey(item.index) : item.index)} + + {#if expandItems[item.index]} + + {/if} + {/each} +
+ {:else} + + {#each visibleItems as item (getKey ? getKey(item.index) : item.index)} + + + + {#if expandItems[item.index]} + + + + {/if} + {/each} +
+ {/if}
diff --git a/src/constants.js b/src/constants.js index 041f75a..d37053d 100644 --- a/src/constants.js +++ b/src/constants.js @@ -24,3 +24,8 @@ export const SCROLL_PROP_LEGACY = { [DIRECTION.VERTICAL]: 'scrollTop', [DIRECTION.HORIZONTAL]: 'scrollLeft', }; + +export const WRAPPER_MODE = { + DIV: 'div', + TABLE: 'table', +}; \ No newline at end of file From d24f72328c4b837896d5c39d9792dc7ec6042195 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Tue, 6 Feb 2024 04:09:43 -0800 Subject: [PATCH 11/17] Change expandItems logic --- src/VirtualList.svelte | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 9b4aafc..7c7b7d9 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -22,7 +22,6 @@ export let items: T[] = []; export let itemCount: number = items.length; - export let expandItems: boolean[] = new Array(items.length || itemCount).fill(false); export let itemSize: ItemSize = 0; export let expandItemSize: ItemSize = 0; export let estimatedItemSize: number = null; @@ -40,6 +39,13 @@ export let mode: WrapperMode = 'div'; + let expandItems: boolean[] = expandItemSize !== 0 ? new Array(items.length || itemCount).fill(false) : []; + + export function onClick(index) { + if (expandItemSize === 0) return; + expandItems[index] = !expandItems[index]; + } + const dispatchEvent = createEventDispatcher(); const sizeAndPositionManager = new SizeAndPositionManager({ @@ -394,11 +400,11 @@ {#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - + {#if expandItems[item.index]} - + {/if} {/each} From cef320fbfb36722b24e42c25ccfd0ef270692dd3 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Mon, 12 Feb 2024 04:20:31 -0800 Subject: [PATCH 12/17] Add active class, overflow: hidden style --- src/VirtualList.svelte | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 7c7b7d9..39989c9 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -359,10 +359,10 @@ if (sticky) { style += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${offset}px;margin-bottom:${-(offset + size)}px;`; - expandStyle += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${expandOffset}px;margin-bottom:${-(expandOffset + expandSize)}px;`; + expandStyle += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${expandOffset}px;margin-bottom:${-(expandOffset + expandSize)}px;overflow: hidden;`; } else { style += `position:absolute;top:${offset}px;`; - expandStyle += `position:absolute;top:${expandOffset}px;`; + expandStyle += `position:absolute;top:${expandOffset}px;overflow: hidden;`; } } else { style = `top:0;width:${size}px;`; @@ -370,10 +370,10 @@ if (sticky) { style += `position:sticky;z-index:1;left:0;margin-left:${offset}px;margin-right:${-(offset + size)}px;`; - expandStyle += `position:sticky;z-index:1;left:0;margin-left:${expandOffset}px;margin-right:${-(expandOffset + expandSize)}px;`; + expandStyle += `position:sticky;z-index:1;left:0;margin-left:${expandOffset}px;margin-right:${-(expandOffset + expandSize)}px;overflow: hidden;`; } else { style += `position:absolute;height:100%;left:${offset}px;`; - expandStyle += `position:absolute;height:100%;left:${expandOffset}px;`; + expandStyle += `position:absolute;height:100%;left:${expandOffset}px;overflow: hidden;`; } } @@ -390,7 +390,7 @@ {#if mode === WRAPPER_MODE.DIV}
{#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - + {#if expandItems[item.index]} {/if} @@ -400,7 +400,7 @@
{#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - + {#if expandItems[item.index]} From b6afb221650d52f3cb1ba849a360ae5dd6764ac1 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Mon, 12 Feb 2024 05:59:16 -0800 Subject: [PATCH 13/17] Update expand Item height style --- src/VirtualList.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 39989c9..a8e3936 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -355,7 +355,7 @@ let style, expandStyle; if (scrollDirection === DIRECTION.VERTICAL) { style = `left:0;width:100%;height:${size}px;`; - expandStyle = `left:0;width:100%;height:${size}px;`; + expandStyle = `left:0;width:100%;height:${expandSize}px;`; if (sticky) { style += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${offset}px;margin-bottom:${-(offset + size)}px;`; @@ -366,7 +366,7 @@ } } else { style = `top:0;width:${size}px;`; - expandStyle = `top:0;width:${size}px;`; + expandStyle = `top:0;width:${expandSize}px;`; if (sticky) { style += `position:sticky;z-index:1;left:0;margin-left:${offset}px;margin-right:${-(offset + size)}px;`; From ae952cefa20d3e26958ae53ab81a5780c31ffd26 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Tue, 13 Feb 2024 00:39:18 -0800 Subject: [PATCH 14/17] Update item, expand item styling --- src/VirtualList.svelte | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index a8e3936..21b8315 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -359,10 +359,10 @@ if (sticky) { style += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${offset}px;margin-bottom:${-(offset + size)}px;`; - expandStyle += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${expandOffset}px;margin-bottom:${-(expandOffset + expandSize)}px;overflow: hidden;`; + expandStyle += `position:sticky;flex-grow:0;z-index:1;top:0;margin-top:${expandOffset}px;margin-bottom:${-(expandOffset + expandSize)}px;`; } else { style += `position:absolute;top:${offset}px;`; - expandStyle += `position:absolute;top:${expandOffset}px;overflow: hidden;`; + expandStyle += `position:absolute;top:${expandOffset}px;`; } } else { style = `top:0;width:${size}px;`; @@ -370,10 +370,10 @@ if (sticky) { style += `position:sticky;z-index:1;left:0;margin-left:${offset}px;margin-right:${-(offset + size)}px;`; - expandStyle += `position:sticky;z-index:1;left:0;margin-left:${expandOffset}px;margin-right:${-(expandOffset + expandSize)}px;overflow: hidden;`; + expandStyle += `position:sticky;z-index:1;left:0;margin-left:${expandOffset}px;margin-right:${-(expandOffset + expandSize)}px;`; } else { style += `position:absolute;height:100%;left:${offset}px;`; - expandStyle += `position:absolute;height:100%;left:${expandOffset}px;overflow: hidden;`; + expandStyle += `position:absolute;height:100%;left:${expandOffset}px;`; } } @@ -390,21 +390,25 @@ {#if mode === WRAPPER_MODE.DIV}
{#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - +
+ +
{#if expandItems[item.index]} - +
+ +
{/if} {/each}
{:else}
{#each visibleItems as item (getKey ? getKey(item.index) : item.index)} - - + + {#if expandItems[item.index]} - + {/if} {/each} From c7e55bc761b2edf2db59c2b742f471a09961af14 Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Mon, 19 Feb 2024 18:25:24 -0800 Subject: [PATCH 15/17] Add "container" new prop --- src/SizeAndPositionManager.js | 2 +- src/VirtualList.svelte | 73 +++++++++++++++++++++++++++-------- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/SizeAndPositionManager.js b/src/SizeAndPositionManager.js index 39b21e9..ee9f6c6 100644 --- a/src/SizeAndPositionManager.js +++ b/src/SizeAndPositionManager.js @@ -341,7 +341,7 @@ export default class SizeAndPositionManager { if (totalSize === 0) { return {}; } - + const maxOffset = Math.max(0, offset || 0) + containerSize; let start = this.findNearestItem(offset); diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 21b8315..6ba222c 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -17,6 +17,8 @@ type ItemSize = number | number[] | ItemSizeGetter; type T = $$Generic; + export let container: string = null; + export let height: number | string = ''; export let width: number | string = '100%'; @@ -105,17 +107,32 @@ onMount(() => { mounted = true; - if ((scrollDirection === DIRECTION.VERTICAL && height) || (scrollDirection === DIRECTION.HORIZONTAL && width)) { - scrollWrapper = wrapper; + if (container) { + scrollWrapper = document.querySelector(container); + } + + if (scrollWrapper) { + scrollWrapper.style.setProperty("height", getVisibleHeight(scrollWrapper) + "px", "important"); + scrollWrapper.style.setProperty("overflow", "auto", "important"); + scrollWrapper.style.setProperty("position", "relative", "important"); + } else { + if ((scrollDirection === DIRECTION.VERTICAL && height) || (scrollDirection === DIRECTION.HORIZONTAL && width)) { + scrollWrapper = wrapper; + scrollWrapper.style.setProperty("overflow", "auto", "important"); + } else { + scrollWrapper = document.querySelector("body"); + } + } + + if (scrollWrapper === document.body) { + window.addEventListener('scroll', handleScroll); } else { - scrollWrapper = window; + scrollWrapper.addEventListener('scroll', handleScroll); } header = wrapper.querySelector('[slot="header"]'); if (header) headerHeight = header.offsetHeight; - - scrollWrapper.addEventListener('scroll', handleScroll); if (scrollOffset != null) { scrollTo(scrollOffset); @@ -126,7 +143,11 @@ onDestroy(() => { if (mounted) { - scrollWrapper.removeEventListener('scroll', handleScroll); + if (scrollWrapper === document.body) { + window.removeEventListener('scroll', handleScroll); + } else { + scrollWrapper.removeEventListener('scroll', handleScroll); + } } }); @@ -210,7 +231,8 @@ function refresh() { const { offset } = state; - const containerSize = getContainerSize(); + const containerSize = getVisibleHeight(scrollWrapper === document.body ? wrapper : scrollWrapper); + const { start, stop } = sizeAndPositionManager.getVisibleRange( Number(containerSize), offset, @@ -219,10 +241,10 @@ let updatedItems = []; const totalSize = sizeAndPositionManager.getTotalSize(); if (scrollDirection === DIRECTION.VERTICAL) { - wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width}; overflow: ${height ? 'auto' : 'hidden'}`; + wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width};`; innerStyle = `flex-direction:column;height:${totalSize}px;`; } else { - wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width}px; overflow: ${width ? 'auto' : 'hidden'}`; + wrapperStyle = `height:${height ? height + 'px' : '100%'};width:${width}px;`; innerStyle = `min-height:100%;width:${totalSize}px;`; } @@ -257,7 +279,7 @@ visibleItems = updatedItems; } - + function scrollTo(value) { if (scrollDirection === DIRECTION.VERTICAL && !height) { @@ -285,7 +307,7 @@ index = 0; } - const containerSize = getContainerSize(); + const containerSize = getVisibleHeight(scrollWrapper === document.body ? wrapper : scrollWrapper); return sizeAndPositionManager.getUpdatedOffsetForIndex( align, Number(containerSize), @@ -322,11 +344,10 @@ function getWrapperOffset() { if ('scroll' in scrollWrapper) { - if (scrollWrapper === window) { - return document.documentElement.scrollTop - wrapper.offsetTop - headerHeight; - } else { - return scrollWrapper.scrollTop - headerHeight; + if (scrollWrapper === document.body) { + return document.documentElement.scrollTop - getDistanceToParent(wrapper, scrollWrapper) - headerHeight; } + return scrollWrapper.scrollTop - getDistanceToParent(wrapper, scrollWrapper) - headerHeight; } else { return scrollWrapper[SCROLL_PROP_LEGACY[scrollDirection]]; } @@ -382,6 +403,26 @@ expandStyle, }; } + + function getVisibleHeight(element) { + if (!element) return; + const rect = element.getBoundingClientRect(); + const visibleTop = Math.max(rect.top, 0); + const visibleBottom = Math.min(rect.bottom, window.innerHeight); + const visibleHeight = visibleBottom - visibleTop; + + return Math.max(visibleHeight, 0); + } + + function getDistanceToParent(child, parent) { + let distance = 0; + let currentElement = child; + while (currentElement && currentElement !== parent && currentElement !== document.body) { + distance += currentElement.offsetTop; + currentElement = currentElement.offsetParent; + } + return distance; + }
@@ -420,7 +461,7 @@ \ No newline at end of file From 818eef20a20ae1007bd68e4a2a1149395e5bad9c Mon Sep 17 00:00:00 2001 From: Lion King917 Date: Thu, 22 Feb 2024 04:21:58 -0800 Subject: [PATCH 17/17] Change scroll wrapper selection logic --- src/VirtualList.svelte | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/VirtualList.svelte b/src/VirtualList.svelte index 2886e10..089234b 100644 --- a/src/VirtualList.svelte +++ b/src/VirtualList.svelte @@ -194,26 +194,26 @@ } function containerPropUpdated() { - if (prevProps.container && scrollWrapper) { - scrollWrapper.classList.remove("virtual-list-container"); - if (originalHeight) { - scrollWrapper.style.setProperty("height", originalHeight); - originalHeight = null; - } - } - - if (container) { - scrollWrapper = document.querySelector(container); - } - - if (scrollWrapper) { - originalHeight = scrollWrapper.style.height; - scrollWrapper.style.setProperty("height", getVisibleHeight(scrollWrapper) + "px", "important"); - scrollWrapper.classList.add("virtual-list-container"); + if ((scrollDirection === DIRECTION.VERTICAL && height) || (scrollDirection === DIRECTION.HORIZONTAL && width)) { + scrollWrapper = wrapper; + scrollWrapper.style.setProperty("overflow", "auto", "important"); } else { - if ((scrollDirection === DIRECTION.VERTICAL && height) || (scrollDirection === DIRECTION.HORIZONTAL && width)) { - scrollWrapper = wrapper; - scrollWrapper.style.setProperty("overflow", "auto", "important"); + if (prevProps.container && scrollWrapper) { + scrollWrapper.classList.remove("virtual-list-container"); + if (originalHeight) { + scrollWrapper.style.setProperty("height", originalHeight); + originalHeight = null; + } + } + + if (container) { + scrollWrapper = document.querySelector(container); + } + + if (scrollWrapper) { + originalHeight = scrollWrapper.style.height; + scrollWrapper.style.setProperty("height", getVisibleHeight(scrollWrapper) + "px", "important"); + scrollWrapper.classList.add("virtual-list-container"); } else { scrollWrapper = document.querySelector("body"); }