44 lines
1.5 KiB
TypeScript
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
|
|
}
|
|
}
|
|
}
|