80 lines
3.2 KiB
JavaScript
Raw Normal View History

/**
* This file contains code for the room list DOM view. It controls rendering elements, the IntersectionObserver
* and lazy loading DOM nodes off-screen to ensure the list doesn't take O(n) to load.
*/
export class List {
/**
* Construct a new List. Children in this list MUST have `id=` attributes which contain sequential numbers.
* @param {string} idPrefix The prefix to remove from the `id=` attribute of children, if any.
* @param {number} debounceTime The number of milliseconds to wait before invoking the callback on intersection change.
* @param {function} callback The callback that will be invoked when the intersection has changed. It is called with
* args for the startIndex, endIndex (inclusive).
*/
constructor(idPrefix, debounceTime, callback) {
this.debounceTimeoutId = null;
this.debounceTime = debounceTime;
this.idPrefix = idPrefix;
this.visibleIndexes = {}; // e.g "44" meaning index 44
this.intersectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
let key = entry.target.id.substring(this.idPrefix.length);
if (entry.isIntersecting) {
this.visibleIndexes[key] = true;
} else {
delete this.visibleIndexes[key];
}
});
// we will process the intersections after a short period of inactivity to not thrash the server
clearTimeout(this.debounceTimeoutId);
this.debounceTimeoutId = setTimeout(() => {
let startIndex = -1;
let endIndex = -1;
Object.keys(this.visibleIndexes).forEach((roomIndex) => {
// e.g "44"
let i = Number(roomIndex);
if (startIndex === -1 || i < startIndex) {
startIndex = i;
}
if (endIndex === -1 || i > endIndex) {
endIndex = i;
}
});
callback(startIndex, endIndex);
}, this.debounceTime);
},
{
threshold: [0],
}
);
}
resize(container, count, createElement) {
let addCount = 0;
let removeCount = 0;
// ensure we have the right number of children, remove or add appropriately.
while (container.childElementCount > count) {
this.intersectionObserver.unobserve(container.lastChild);
container.removeChild(container.lastChild);
removeCount += 1;
}
for (let i = container.childElementCount; i < count; i++) {
const cell = createElement(i);
container.appendChild(cell);
this.intersectionObserver.observe(cell);
addCount += 1;
}
if (addCount > 0 || removeCount > 0) {
console.log(
"resize: added ",
addCount,
"nodes, removed",
removeCount,
"nodes"
);
}
}
}