|
1 | 1 | // TODO Automatically disconnect the observer when the returned DIV is detached.
|
2 |
| -export function resize(run: (width: number, height: number) => Node, invalidation?: Promise<void>): Node { |
| 2 | +export function resize( |
| 3 | + render: (width: number, height: number) => Node | null | Promise<Node | null>, |
| 4 | + invalidation?: Promise<void> |
| 5 | +): Node { |
3 | 6 | const div = document.createElement("div");
|
4 | 7 | div.style.position = "relative";
|
5 |
| - if (run.length !== 1) div.style.height = "100%"; |
6 |
| - const observer = new ResizeObserver(([entry]) => { |
| 8 | + if (render.length !== 1) div.style.height = "100%"; |
| 9 | + let currentRender = 0; |
| 10 | + let currentDisplay = 0; |
| 11 | + const observer = new ResizeObserver(async ([entry]) => { |
7 | 12 | const {width, height} = entry.contentRect;
|
| 13 | + const childRender = ++currentRender; |
| 14 | + const child = width > 0 ? await render(width, height) : null; // don’t render if detached |
| 15 | + if (currentDisplay > childRender) return; // ignore stale renders |
| 16 | + currentDisplay = childRender; |
8 | 17 | while (div.lastChild) div.lastChild.remove();
|
9 |
| - if (width > 0) { |
10 |
| - const child = run(width, height); |
11 |
| - // prevent feedback loop if height is used |
12 |
| - if (run.length !== 1 && isElement(child)) child.style.position = "absolute"; |
13 |
| - div.append(child); |
14 |
| - } |
| 18 | + if (child == null) return; // clear if nullish |
| 19 | + if (render.length !== 1 && isElement(child)) child.style.position = "absolute"; // prevent feedback loop if height is used |
| 20 | + div.append(child); |
15 | 21 | });
|
16 | 22 | observer.observe(div);
|
17 | 23 | invalidation?.then(() => observer.disconnect());
|
|
0 commit comments