|
78 | 78 | // reactive variable related to positioning |
79 | 79 | scrollToIndex, |
80 | 80 | scrollToOffset, |
81 | | - // windowOverPadding = 3, |
82 | 81 |
|
83 | 82 | // Render count at start, used for SSR |
84 | 83 | preRenderCount = 6, |
|
100 | 99 | class: className = '', |
101 | 100 | style = '', |
102 | 101 |
|
103 | | - // calculate the size of a given index |
| 102 | + // calculates the size of a given index |
104 | 103 | sizingCalculator |
105 | 104 | }: { |
106 | 105 | items: any[]; |
|
138 | 137 |
|
139 | 138 | // ======== VARIABLES ======== |
140 | 139 |
|
| 140 | + // number of elements to pad above & below the visible range to prevent glitching |
| 141 | + const WINDOW_PASSING_COUNT = 3; |
| 142 | +
|
141 | 143 | let mounted: boolean = false; |
142 | 144 |
|
| 145 | + let lastMeasuredIndex = -1; |
| 146 | +
|
143 | 147 | // dom references |
144 | 148 | let listContainer: HTMLDivElement; |
145 | 149 | // svelte-ignore non_reactive_update - not sure where its updated?! |
|
148 | 152 | let clientHeight: number = $state(0); |
149 | 153 | let clientWidth: number = $state(0); |
150 | 154 |
|
151 | | - // viewport overfetch trigger in px |
152 | | - // let overfetchBufferInPx: number = 100; |
153 | | -
|
154 | 155 | let itemKey: 'index' | ((item: any, index: number) => any); |
155 | 156 |
|
156 | 157 | // virtual list first visible index |
|
208 | 209 | } |
209 | 210 | const r: VLSlotSignature[] = []; |
210 | 211 | for (let index = startIdx; index <= end2; index++) { |
211 | | - // console.log(index); |
212 | 212 | const item = items[index]; |
213 | 213 | if (item) { |
214 | | - // console.log(sizes[index]) |
215 | 214 | r.push({ item, index: index, size: sizes[index] }); |
216 | 215 | } |
217 | 216 | } |
|
284 | 283 |
|
285 | 284 | if (prevState?.offset !== offset || prevState?.scrollChangeReason !== scrollChangeReason) { |
286 | 285 | refreshOffsets(); |
287 | | -
|
288 | | - // const vr = getVisibleRange(isHorizontal ? clientWidth : clientHeight, offset); |
289 | | - // onVisibleRangeUpdate?.({ start: vr.start, end: vr.end }); |
290 | 286 | } |
291 | 287 |
|
292 | 288 | if (prevState?.offset !== offset && scrollChangeReason === SCROLL_CHANGE_REASON.REQUESTED) { |
|
297 | 293 | }); |
298 | 294 |
|
299 | 295 | $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 |
304 | 297 | console.log('component is resized ' + clientHeight + ' ' + clientWidth); |
305 | 298 | if (mounted) recomputeSizes(0); // call scroll.reset |
306 | 299 | }); |
|
387 | 380 | onAfterScroll?.({ offset, event }); |
388 | 381 | } |
389 | 382 |
|
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 | | -
|
408 | 383 | function getOffsetForIndex( |
409 | 384 | index: number, |
410 | 385 | align: ALIGNMENT = scrollToAlignment, |
|
443 | 418 |
|
444 | 419 | // const datum = this.getSizeAndPositionForIndex(targetIndex); |
445 | 420 |
|
446 | | - const datumS = sizes[targetIndex]; |
447 | | -
|
| 421 | + const size = sizes[targetIndex]; |
448 | 422 | const maxOffset = offsets[targetIndex]; |
449 | | - const minOffset = maxOffset - containerSize + datumS; |
| 423 | + const minOffset = maxOffset - containerSize + size; |
450 | 424 |
|
451 | 425 | let idealOffset; |
452 | 426 |
|
|
455 | 429 | idealOffset = minOffset; |
456 | 430 | break; |
457 | 431 | case ALIGNMENT.CENTER: |
458 | | - idealOffset = maxOffset - (containerSize - datumS) / 2; |
| 432 | + idealOffset = maxOffset - (containerSize - size) / 2; |
459 | 433 | break; |
460 | 434 | case ALIGNMENT.START: |
461 | 435 | idealOffset = maxOffset; |
|
475 | 449 | * This allows partially visible items (with offsets just before/above the fold) to be visible. |
476 | 450 | * |
477 | 451 | */ |
478 | | - let lastMeasuredIndex = -1; |
479 | 452 |
|
480 | 453 | function findNearestItem(offset: number): number { |
481 | 454 | if (isNaN(offset)) { |
|
543 | 516 |
|
544 | 517 | // recalculates the viewport position |
545 | 518 | function refreshOffsets() { |
546 | | - // console.log('updatePositions'); |
547 | 519 | if (!avgSizeInPx) { |
548 | 520 | avgSizeInPx = getAvgSize(); |
549 | 521 | } |
550 | 522 |
|
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 | +
|
553 | 529 | startIdx = vr.start; |
554 | 530 | endIdx = vr.end; |
555 | 531 |
|
|
660 | 636 | const style = getComputedStyle(el); |
661 | 637 |
|
662 | 638 | 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) { |
670 | 640 | r += |
671 | 641 | parseFloat(style.borderLeftWidth) + |
672 | 642 | parseFloat(style.borderRightWidth) + |
673 | 643 | parseFloat(style.marginLeft) + |
674 | 644 | parseFloat(style.marginRight); |
| 645 | + } else { |
| 646 | + r += |
| 647 | + parseFloat(style.borderTopWidth) + |
| 648 | + parseFloat(style.borderBottomWidth) + |
| 649 | + parseFloat(style.marginTop) + |
| 650 | + parseFloat(style.marginBottom); |
675 | 651 | } |
676 | 652 | return Number.isNaN(r) ? 0 : r; |
677 | 653 | } |
678 | 654 |
|
679 | 655 | function getScroll(el: HTMLElement) { |
680 | | - return !isHorizontal ? el.scrollTop : el.scrollLeft; |
| 656 | + return isHorizontal ? el.scrollLeft : el.scrollTop; |
681 | 657 | } |
682 | 658 |
|
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 | + // } |
687 | 663 |
|
688 | | - // scrolls the contrainer to px |
| 664 | + // scrolls the contrainer to give px value |
689 | 665 | function scrollTo(value: number) { |
690 | 666 | if ('scroll' in listContainer) { |
691 | 667 | const p: Record<string, any> = { behavior: scrollToBehaviour }; |
692 | | - p[!isHorizontal ? 'top' : 'left'] = value; |
| 668 | + p[isHorizontal ? 'left' : 'top'] = value; |
693 | 669 | listContainer.scroll(p); |
694 | 670 | } else { |
695 | 671 | //@ts-expect-error no index signature |
696 | | - listContainer[!isHorizontal ? 'scrollTop' : 'scrollLeft'] = value; |
| 672 | + listContainer[isHorizontal ? 'scrollLeft' : 'scrollTop'] = value; |
697 | 673 | } |
698 | 674 | } |
699 | 675 |
|
|
0 commit comments