|
| 1 | +<script lang="ts"> |
| 2 | + import { onMount, tick } from "svelte" |
| 3 | + import View from "../ol/View.svelte" |
| 4 | +
|
| 5 | + export let items: any[] |
| 6 | + export let height = '100%' |
| 7 | +
|
| 8 | + export let scrollPos: number = 0 |
| 9 | +
|
| 10 | + let heightMap = new WeakMap<any, { |
| 11 | + offset: number, |
| 12 | + height: number |
| 13 | + }>(); |
| 14 | +
|
| 15 | + let viewport: HTMLElement |
| 16 | + let viewportHeight = 0 |
| 17 | +
|
| 18 | + let contents: HTMLElement; |
| 19 | +
|
| 20 | + let top = 0 |
| 21 | + let bottom = 0 |
| 22 | + let averageHeight = 0 |
| 23 | +
|
| 24 | + let startHeight = 0 |
| 25 | + let start = 0 |
| 26 | + let endHeight = 0 |
| 27 | + let end = 0 |
| 28 | +
|
| 29 | + let mounted = false |
| 30 | +
|
| 31 | + let rows: any |
| 32 | + $: visible = items.slice(start, end).map((data, i) => ({ |
| 33 | + index: i + start, |
| 34 | + data |
| 35 | + })) |
| 36 | +
|
| 37 | + $: if(mounted) updateStartAndEnd(top, bottom, items) |
| 38 | +
|
| 39 | + async function updateStartAndEnd(top, bottom, items) { |
| 40 | + if (top === bottom) return |
| 41 | +
|
| 42 | + let y = 0 |
| 43 | + for (let i = 0; i < items.length; ++i) { |
| 44 | + if (y >= bottom) break |
| 45 | +
|
| 46 | + if (!heightMap.has(items[i])) { |
| 47 | + // update start/end and render row |
| 48 | + if (y <= top) { |
| 49 | + start = i |
| 50 | + startHeight = y |
| 51 | + } |
| 52 | + end = i + 1 |
| 53 | +
|
| 54 | + await tick() |
| 55 | +
|
| 56 | + console.log(`Rendered ${start} to ${end}`) |
| 57 | +
|
| 58 | + const row = contents.querySelector(`l-row[data-row="${i}"]`) |
| 59 | + if (!row) { |
| 60 | + console.log(contents.childNodes) |
| 61 | + console.log("Something went wrong!") |
| 62 | + break |
| 63 | + } |
| 64 | + heightMap.set(items[i], { |
| 65 | + offset: row['offsetHeight'], |
| 66 | + height: row.clientHeight |
| 67 | + }) |
| 68 | + } |
| 69 | +
|
| 70 | + const info = heightMap.get(items[i]) |
| 71 | + if (y <= top && y + info.height >= top) { |
| 72 | + start = i |
| 73 | + startHeight = y |
| 74 | + } |
| 75 | +
|
| 76 | + endHeight = y |
| 77 | + end = i + 1 |
| 78 | +
|
| 79 | + if (y > bottom) { |
| 80 | + break |
| 81 | + } |
| 82 | + } |
| 83 | +
|
| 84 | + console.log(`Start: ${start}, End: ${end}, Top: ${top}, Bottom: ${bottom}`) |
| 85 | + } |
| 86 | +
|
| 87 | + function onScroll() { |
| 88 | + top = viewport.scrollTop |
| 89 | + bottom = top + viewport.clientHeight |
| 90 | + scrollPos = top |
| 91 | + } |
| 92 | +
|
| 93 | + onMount(() => { |
| 94 | + mounted = true |
| 95 | + rows = contents.getElementsByTagName('l-row') |
| 96 | + onScroll() |
| 97 | + }) |
| 98 | +</script> |
| 99 | + |
| 100 | +<style> |
| 101 | + l-viewport { |
| 102 | + position: relative; |
| 103 | + overflow-y: auto; |
| 104 | + -webkit-overflow-scrolling: touch; |
| 105 | + display: block; |
| 106 | + } |
| 107 | +
|
| 108 | + l-contents, l-row { |
| 109 | + display: block; |
| 110 | + } |
| 111 | +
|
| 112 | + l-row { |
| 113 | + overflow: hidden; |
| 114 | + } |
| 115 | +</style> |
| 116 | + |
| 117 | +<l-viewport |
| 118 | + bind:this={viewport} |
| 119 | + bind:offsetHeight={viewportHeight} |
| 120 | + on:scroll={onScroll} |
| 121 | + style:height="{height}"> |
| 122 | + <l-contents bind:this={contents} |
| 123 | + style:padding-top="{startHeight}px" |
| 124 | + style:padding-bottom="{bottom}px"> |
| 125 | + {#each visible as row (row.index)} |
| 126 | + <l-row data-row={row.index}> |
| 127 | + <slot item={row.data}>No template</slot> |
| 128 | + </l-row> |
| 129 | + {/each} |
| 130 | + |
| 131 | + </l-contents> |
| 132 | +</l-viewport> |
0 commit comments