|
13 | 13 | .item { display: flex; align-items: center; padding: 0 16px; font-size: 14px; border-bottom: 1px solid #f3f4f6; box-sizing: border-box; } |
14 | 14 | .item-even { background: #f9fafb; } |
15 | 15 | .item-odd { background: #ffffff; } |
16 | | - .loading { color: #9ca3af; font-style: italic; font-size: 13px; } |
17 | | - .end { color: #6b7280; font-size: 13px; } |
18 | | - .status { font-size: 12px; color: #9ca3af; margin-top: 8px; text-align: right; } |
| 16 | + .status { color: #9ca3af; font-style: italic; font-size: 13px; } |
| 17 | + .footer { font-size: 12px; color: #9ca3af; margin-top: 8px; text-align: right; } |
19 | 18 | </style> |
20 | 19 | </head> |
21 | 20 | <body> |
22 | 21 | <h1>Infinite Scroll Virtualisation</h1> |
23 | | - <p>Rows are fetched in pages as you scroll toward the end.</p> |
| 22 | + <p>Rows are fetched in pages as you scroll toward the end of the list.</p> |
24 | 23 |
|
25 | | - static async function fetchPage(offset: number, limit: number) { |
26 | | - await new Promise(r => setTimeout(r, 500)) |
27 | | - const total = 500 |
28 | | - const rows = Array.from({ length: Math.min(limit, total - offset) }, (_, i) => |
29 | | - `Row #${offset + i + 1} — loaded at offset ${offset}` |
30 | | - ) |
31 | | - return { rows, nextOffset: offset + rows.length, hasMore: offset + rows.length < total } |
32 | | - } |
| 24 | + <const/TOTAL = 500/> |
| 25 | + <const/PAGE_SIZE = 20/> |
33 | 26 |
|
34 | 27 | <let/rows = ([] as string[])/> |
35 | 28 | <let/hasMore = true/> |
36 | 29 | <let/loading = false/> |
37 | | - <let/offset = 0/> |
| 30 | + <let/nextOffset = 0/> |
38 | 31 | <let/mounted = false/> |
39 | 32 |
|
40 | | - <script() { |
41 | | - mounted = true |
42 | | - loading = true |
43 | | - fetchPage(0, 20).then(result => { |
44 | | - rows = result.rows |
45 | | - offset = result.nextOffset |
46 | | - hasMore = result.hasMore |
47 | | - loading = false |
48 | | - }) |
49 | | - }/> |
50 | | - |
51 | | - <const/loadMore = async () => { |
| 33 | + <const/loadPage = async () => { |
52 | 34 | if (loading || !hasMore) return |
53 | 35 | loading = true |
54 | | - const result = await fetchPage(offset, 20) |
55 | | - rows = [...rows, ...result.rows] |
56 | | - offset = result.nextOffset |
57 | | - hasMore = result.hasMore |
| 36 | + await new Promise<void>(r => setTimeout(r, 500)) |
| 37 | + const offset = nextOffset |
| 38 | + const count = Math.min(PAGE_SIZE, TOTAL - offset) |
| 39 | + const newRows = Array.from({ length: count }, (_, k) => |
| 40 | + "Row #" + (offset + k + 1) + " — loaded at offset " + offset |
| 41 | + ) |
| 42 | + rows = [...rows, ...newRows] |
| 43 | + nextOffset = offset + count |
| 44 | + hasMore = nextOffset < TOTAL |
58 | 45 | loading = false |
59 | 46 | }/> |
60 | 47 |
|
| 48 | + <script() { |
| 49 | + mounted = true |
| 50 | + loadPage() |
| 51 | + }/> |
| 52 | + |
61 | 53 | <const/count = hasMore ? rows.length + 1 : rows.length/> |
62 | 54 |
|
63 | 55 | <div/scrollEl class="scroll-container"> |
|
69 | 61 | overscan=5 |
70 | 62 | > |
71 | 63 | <script() { |
72 | | - const lastItem = virtualItems[virtualItems.length - 1] |
73 | | - if (lastItem && lastItem.index >= rows.length - 1) loadMore() |
| 64 | + const last = virtualItems[virtualItems.length - 1] |
| 65 | + if (last && last.index >= rows.length - 1) loadPage() |
74 | 66 | }/> |
75 | 67 |
|
76 | 68 | <div style=`height: ${totalSize}px; width: 100%; position: relative`> |
|
83 | 75 | ${rows[item.index]} |
84 | 76 | </if> |
85 | 77 | <else> |
86 | | - <if=loading> |
87 | | - <span class="loading">Loading more…</span> |
88 | | - </if> |
89 | | - <else> |
90 | | - <span class="end">${hasMore ? "Scroll to load more" : `All ${rows.length} rows loaded`}</span> |
91 | | - </else> |
| 78 | + <span class="status">${loading ? "Loading more…" : hasMore ? "Scroll to load more" : "All rows loaded"}</span> |
92 | 79 | </else> |
93 | 80 | </div> |
94 | 81 | </for> |
|
97 | 84 | </if> |
98 | 85 | </div> |
99 | 86 |
|
100 | | - <p class="status"> |
101 | | - ${rows.length} rows loaded${hasMore ? " · more available" : " · end of list"} |
| 87 | + <p class="footer"> |
| 88 | + ${rows.length} of ${TOTAL} rows loaded |
102 | 89 | </p> |
103 | 90 | </body> |
104 | 91 | </html> |
0 commit comments