Menu
Public repository / 054e5cda0171bd13facff3c16a38f2048a1052cc
AkurAI-Mail / src/lib/webmail/virtual.js
Latest change 10d19fe0941c186ec244d1f0e2af6b19200dcb0c - OLI-248: pure webmail logic as plain ES modules with JSDoc types; inline icon module by Ólafur Búi Ólafsson
/** Variable-height list windowing for the message list. */
/** Prefix sums: offsets[i] is the top of item i; offsets[n] is the total height. */
/**
* Cumulative row offsets: `offsets[i]` is where item i starts, `offsets[n]` the total height.
* @template T
* @param {T[]} items
* @param {(item: T) => number} height
* @returns {number[]}
*/
export function threadOffsets(items, height) {
const offsets = new Array (items.length + 1);
offsets[0] = 0;
for (let i = 0; i < items.length; i++) offsets[i + 1] = offsets[i] + height(items[i]);
return offsets;
}
/**
* Index of the item containing pixel `y`.
* @param {number[]} offsets
* @param {number} y
*/
export function indexAt(offsets, y) {
const count = offsets.length - 1;
let low = 0;
let high = count - 1;
while (low < high) {
const mid = (low + high) >> 1;
if (offsets[mid + 1] > y) high = mid;
else low = mid + 1;
}
return count ? low : 0;
}
/**
* Items to render for a viewport, padded by `overscan` items on each side.
* @param {number[]} offsets
* @param {number} scrollTop
* @param {number} viewportHeight
* @param {number} overscan
*/
export function visibleRange(offsets, scrollTop, viewportHeight, overscan) {
const count = offsets.length - 1;
const start = Math.max(0, indexAt(offsets, scrollTop) - overscan);
const bottom = scrollTop + viewportHeight;
let end = start;
while (end < count && offsets[end] < bottom) end++;
return { start, end: Math.min(count, end + overscan) };
}