Files
apes-sidebar/src/client/open-when-sized.ts
T

44 lines
1.5 KiB
TypeScript

/**
* Deferred one-shot open for hosts that may not have a real size yet.
*
* xterm's `Terminal.open()` must not run in a zero-size container: the
* renderer creation fails there (the DomRenderer is built from the host's
* dimensions), leaving the render service's renderer `undefined`, and the
* next Viewport refresh crashes reading `.dimensions` off it. WebKit-based
* hosts (WKWebView) reliably report zero while the bottom panel's expand
* slide is in flight; any `display:none`-hidden ancestor does the same.
*
* The caller's `open` callback (open + fit + resize) is invoked exactly
* once, on the first frame where the host reports a real size. While the
* host stays zero-sized the polling continues every frame; it stops when
* the host leaves the document (`isConnected`), so a pending open never
* fires after unmount. The returned cancel function drops a pending frame
* immediately (idempotent).
*
* `raf`/`caf` are injectable so tests can drive the polling deterministically.
*/
export function openWhenSized(
host: HTMLElement,
open: () => void,
raf: (cb: FrameRequestCallback) => number = requestAnimationFrame,
caf: (id: number) => void = cancelAnimationFrame,
): () => void {
let frame: number | null = null
const step = (): void => {
frame = null
if (!host.isConnected) return
if (host.clientWidth > 0 && host.clientHeight > 0) {
open()
return
}
frame = raf(step)
}
frame = raf(step)
return () => {
if (frame !== null) {
caf(frame)
frame = null
}
}
}