Skip to content

Commit e8a924b

Browse files
committed
fix: better implementation
1 parent e875753 commit e8a924b

File tree

26 files changed

+68
-429
lines changed

26 files changed

+68
-429
lines changed

KNOWN-ISSUES.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
### Using @sveltejs/package > 2.3.0 will generate "ts" rather than transpiled outputs, resulting in ts files being pushed to npm.
22

33
For now it breaks Svelte preview REPL as it can't compile ts on the fly
4-

src/comp/ToggleButton.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
}: {
77
pressed: boolean;
88
label: string;
9-
onclick?: Function;
9+
onclick?: () => void;
1010
} = $props();
1111
1212
function clicked() {

src/lib/new/VirtualListNew.svelte renamed to src/lib/VirtualListNew.svelte

Lines changed: 31 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
// reactive variable related to positioning
7979
scrollToIndex,
8080
scrollToOffset,
81-
// windowOverPadding = 3,
8281
8382
// Render count at start, used for SSR
8483
preRenderCount = 6,
@@ -100,7 +99,7 @@
10099
class: className = '',
101100
style = '',
102101
103-
// calculate the size of a given index
102+
// calculates the size of a given index
104103
sizingCalculator
105104
}: {
106105
items: any[];
@@ -138,8 +137,13 @@
138137
139138
// ======== VARIABLES ========
140139
140+
// number of elements to pad above & below the visible range to prevent glitching
141+
const WINDOW_PASSING_COUNT = 3;
142+
141143
let mounted: boolean = false;
142144
145+
let lastMeasuredIndex = -1;
146+
143147
// dom references
144148
let listContainer: HTMLDivElement;
145149
// svelte-ignore non_reactive_update - not sure where its updated?!
@@ -148,9 +152,6 @@
148152
let clientHeight: number = $state(0);
149153
let clientWidth: number = $state(0);
150154
151-
// viewport overfetch trigger in px
152-
// let overfetchBufferInPx: number = 100;
153-
154155
let itemKey: 'index' | ((item: any, index: number) => any);
155156
156157
// virtual list first visible index
@@ -208,10 +209,8 @@
208209
}
209210
const r: VLSlotSignature[] = [];
210211
for (let index = startIdx; index <= end2; index++) {
211-
// console.log(index);
212212
const item = items[index];
213213
if (item) {
214-
// console.log(sizes[index])
215214
r.push({ item, index: index, size: sizes[index] });
216215
}
217216
}
@@ -284,9 +283,6 @@
284283
285284
if (prevState?.offset !== offset || prevState?.scrollChangeReason !== scrollChangeReason) {
286285
refreshOffsets();
287-
288-
// const vr = getVisibleRange(isHorizontal ? clientWidth : clientHeight, offset);
289-
// onVisibleRangeUpdate?.({ start: vr.start, end: vr.end });
290286
}
291287
292288
if (prevState?.offset !== offset && scrollChangeReason === SCROLL_CHANGE_REASON.REQUESTED) {
@@ -297,10 +293,7 @@
297293
});
298294
299295
$effect(() => {
300-
// listen to updates:
301-
//@ts-expect-error unused no side effect
302-
//clientHeight, clientWidth;
303-
// on update:
296+
//TODO there is a bug with client sizing
304297
console.log('component is resized ' + clientHeight + ' ' + clientWidth);
305298
if (mounted) recomputeSizes(0); // call scroll.reset
306299
});
@@ -387,24 +380,6 @@
387380
onAfterScroll?.({ offset, event });
388381
}
389382
390-
// return the index of the starting boundary
391-
// function getStart(): number {
392-
// const startPosition =
393-
// getScroll(listContainer) - getPaddingStart(listContainer) - overfetchBufferInPx;
394-
// return findNearestItem(startPosition);
395-
// }
396-
397-
// return the index of the closing boundary
398-
// function getEnd() {
399-
// const endPosition =
400-
// getScroll(listContainer) -
401-
// getPaddingStart(listContainer) +
402-
// getClientSize(listContainer) +
403-
// overfetchBufferInPx;
404-
405-
// return findNearestItem(endPosition);
406-
// }
407-
408383
function getOffsetForIndex(
409384
index: number,
410385
align: ALIGNMENT = scrollToAlignment,
@@ -443,10 +418,9 @@
443418
444419
// const datum = this.getSizeAndPositionForIndex(targetIndex);
445420
446-
const datumS = sizes[targetIndex];
447-
421+
const size = sizes[targetIndex];
448422
const maxOffset = offsets[targetIndex];
449-
const minOffset = maxOffset - containerSize + datumS;
423+
const minOffset = maxOffset - containerSize + size;
450424
451425
let idealOffset;
452426
@@ -455,7 +429,7 @@
455429
idealOffset = minOffset;
456430
break;
457431
case ALIGNMENT.CENTER:
458-
idealOffset = maxOffset - (containerSize - datumS) / 2;
432+
idealOffset = maxOffset - (containerSize - size) / 2;
459433
break;
460434
case ALIGNMENT.START:
461435
idealOffset = maxOffset;
@@ -475,7 +449,6 @@
475449
* This allows partially visible items (with offsets just before/above the fold) to be visible.
476450
*
477451
*/
478-
let lastMeasuredIndex = -1;
479452
480453
function findNearestItem(offset: number): number {
481454
if (isNaN(offset)) {
@@ -543,13 +516,16 @@
543516
544517
// recalculates the viewport position
545518
function refreshOffsets() {
546-
// console.log('updatePositions');
547519
if (!avgSizeInPx) {
548520
avgSizeInPx = getAvgSize();
549521
}
550522
551-
const vr = getVisibleRange(isHorizontal ? clientWidth : clientHeight, curState.offset,3);
552-
523+
const vr = getVisibleRange(
524+
isHorizontal ? clientWidth : clientHeight,
525+
curState.offset,
526+
WINDOW_PASSING_COUNT
527+
);
528+
553529
startIdx = vr.start;
554530
endIdx = vr.end;
555531
@@ -660,40 +636,40 @@
660636
const style = getComputedStyle(el);
661637
662638
let r = getClientSize(el);
663-
if (!isHorizontal) {
664-
r +=
665-
parseFloat(style.borderTopWidth) +
666-
parseFloat(style.borderBottomWidth) +
667-
parseFloat(style.marginTop) +
668-
parseFloat(style.marginBottom);
669-
} else {
639+
if (isHorizontal) {
670640
r +=
671641
parseFloat(style.borderLeftWidth) +
672642
parseFloat(style.borderRightWidth) +
673643
parseFloat(style.marginLeft) +
674644
parseFloat(style.marginRight);
645+
} else {
646+
r +=
647+
parseFloat(style.borderTopWidth) +
648+
parseFloat(style.borderBottomWidth) +
649+
parseFloat(style.marginTop) +
650+
parseFloat(style.marginBottom);
675651
}
676652
return Number.isNaN(r) ? 0 : r;
677653
}
678654
679655
function getScroll(el: HTMLElement) {
680-
return !isHorizontal ? el.scrollTop : el.scrollLeft;
656+
return isHorizontal ? el.scrollLeft : el.scrollTop;
681657
}
682658
683-
function getPaddingStart(el: HTMLElement) {
684-
const style = getComputedStyle(el);
685-
return !isHorizontal ? parseFloat(style.paddingTop) : parseFloat(style.paddingLeft);
686-
}
659+
// function getPaddingStart(el: HTMLElement) {
660+
// const style = getComputedStyle(el);
661+
// return isHorizontal ? parseFloat(style.paddingLeft) : parseFloat(style.paddingTop);
662+
// }
687663
688-
// scrolls the contrainer to px
664+
// scrolls the contrainer to give px value
689665
function scrollTo(value: number) {
690666
if ('scroll' in listContainer) {
691667
const p: Record<string, any> = { behavior: scrollToBehaviour };
692-
p[!isHorizontal ? 'top' : 'left'] = value;
668+
p[isHorizontal ? 'left' : 'top'] = value;
693669
listContainer.scroll(p);
694670
} else {
695671
//@ts-expect-error no index signature
696-
listContainer[!isHorizontal ? 'scrollTop' : 'scrollLeft'] = value;
672+
listContainer[isHorizontal ? 'scrollLeft' : 'scrollTop'] = value;
697673
}
698674
}
699675

src/lib/index.ts

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
export type VirtualItemSize = number | number[] | ((item: unknown, index: number) => number);
2-
3-
export { default as VirtualList } from './VirtualList.svelte';
1+
export { default as VirtualList } from './VirtualListNew.svelte';
42

3+
// sizing calculator signature, used to programmatically size elements. recommendation: stick to css and defaults
54
export type SizingCalculatorFn = (index: number, item: unknown) => number;
65

7-
// use by the row() snippet
6+
// used by the vl_slot() snippet
87
export interface VLSlotSignature {
9-
// the actual item value
10-
item: any;
11-
128
// The row's index being rendered, from the original dataset
139
// The index is a string if the IDs are processed via the getKey() function
1410
index: number | string;
1511

16-
// only there if there a custom sizing calculator, calculated size in px
12+
// the item being rendered
13+
item: any;
14+
15+
// only present if there a custom sizing calculator configured, holds calculated size in pixels
1716
size?: number;
1817
}
1918

@@ -24,12 +23,7 @@ export interface VLRange {
2423
end: number;
2524
}
2625

27-
export interface VLRangeEvent extends VLRange {
28-
type: 'range.update';
29-
}
30-
31-
export interface AfterScrollEvent {
32-
type: 'scroll.update';
26+
export interface VLScrollEvent {
3327
// either the value of `wrapper.scrollTop` or `wrapper.scrollLeft`
3428
offset: number | string;
3529
// the original event
@@ -48,13 +42,3 @@ export enum ALIGNMENT {
4842
CENTER = 'center',
4943
END = 'end'
5044
}
51-
52-
export enum DIRECTION {
53-
HORIZONTAL = 'horizontal',
54-
VERTICAL = 'vertical'
55-
}
56-
57-
export enum SCROLL_CHANGE_REASON {
58-
OBSERVED = 0,
59-
REQUESTED = 1
60-
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/lib/new/index.ts renamed to src/lib/old/index.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// export type VirtualItemSize = number | number[] | ((item: unknown, index: number) => number);
1+
export { default as VirtualList } from './VirtualList.svelte';
22

3-
export { default as VirtualList } from './VirtualListNew.svelte';
3+
export type VirtualItemSize = number | number[] | ((item: unknown, index: number) => number);
44

55
export type SizingCalculatorFn = (index: number, item: unknown) => number;
66

@@ -24,11 +24,12 @@ export interface VLRange {
2424
end: number;
2525
}
2626

27-
// export interface VLRangeEvent extends VLRange {
28-
// type: 'range.update';
29-
// }
27+
export interface VLRangeEvent extends VLRange {
28+
type: 'range.update';
29+
}
3030

31-
export interface VLScrollEvent {
31+
export interface AfterScrollEvent {
32+
type: 'scroll.update';
3233
// either the value of `wrapper.scrollTop` or `wrapper.scrollLeft`
3334
offset: number | string;
3435
// the original event
@@ -48,7 +49,12 @@ export enum ALIGNMENT {
4849
END = 'end'
4950
}
5051

51-
// export enum DIRECTION {
52-
// HORIZONTAL = 'horizontal',
53-
// VERTICAL = 'vertical'
54-
// }
52+
export enum DIRECTION {
53+
HORIZONTAL = 'horizontal',
54+
VERTICAL = 'vertical'
55+
}
56+
57+
export enum SCROLL_CHANGE_REASON {
58+
OBSERVED = 0,
59+
REQUESTED = 1
60+
}

src/routes/+layout.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{ title: 'Horizontal', path: '/examples/horizontal' },
2929
{ title: 'Table', path: '/examples/table' },
3030
{ title: 'Variable Sizing', path: '/examples/variablesizing' },
31-
{ title: 'Positioning', path: '/examples/positioningNew' },
31+
{ title: 'Positioning', path: '/examples/positioning' },
3232
{ title: 'OLDPositioning', path: '/examples/positioningOLD' },
3333
{ title: 'Events', path: '/examples/events' }
3434
]

src/routes/examples/horizontal/code.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<script lang="ts">
2-
import VirtualList from 'svelte-virtuallists/new/VirtualListNew.svelte';
2+
import { VirtualList, type VLSlotSignature } from 'svelte-virtuallists';
33
44
const myModel = new Array(10000).fill(1).map((v, i) => {
55
return { text: 'ITEM ' + i + ' - Item ' + i };
66
});
77
</script>
88

99
<VirtualList items={myModel} style="width:100%" isHorizontal={true}>
10-
{#snippet vl_slot({ item, index })}
10+
{#snippet vl_slot({ item }: VLSlotSignature)}
1111
<div style="border: 1px solid rgb(204, 204, 204)">
1212
{item.text}
1313
</div>

0 commit comments

Comments
 (0)