55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import type { DOMElement } from './dom.js'
|
|
import type { Rectangle } from './layout/geometry.js'
|
|
|
|
/**
|
|
* Cached layout bounds for each rendered node (used for blit + clearing).
|
|
* `top` is the yoga-local getComputedTop() — stored so ScrollBox viewport
|
|
* culling can skip yoga reads for clean children whose position hasn't
|
|
* shifted (O(dirty) instead of O(mounted) first-pass).
|
|
*/
|
|
export type CachedLayout = {
|
|
x: number
|
|
y: number
|
|
width: number
|
|
height: number
|
|
top?: number
|
|
}
|
|
|
|
export const nodeCache = new WeakMap<DOMElement, CachedLayout>()
|
|
|
|
/** Rects of removed children that need clearing on next render */
|
|
export const pendingClears = new WeakMap<DOMElement, Rectangle[]>()
|
|
|
|
/**
|
|
* Set when a pendingClear is added for an absolute-positioned node.
|
|
* Signals renderer to disable blit for the next frame: the removed node
|
|
* may have painted over non-siblings (e.g. an overlay over a ScrollBox
|
|
* earlier in tree order), so their blits from prevScreen would restore
|
|
* the overlay's pixels. Normal-flow removals are already handled by
|
|
* hasRemovedChild at the parent level; only absolute positioning paints
|
|
* cross-subtree. Reset at the start of each render.
|
|
*/
|
|
let absoluteNodeRemoved = false
|
|
|
|
export function addPendingClear(
|
|
parent: DOMElement,
|
|
rect: Rectangle,
|
|
isAbsolute: boolean,
|
|
): void {
|
|
const existing = pendingClears.get(parent)
|
|
if (existing) {
|
|
existing.push(rect)
|
|
} else {
|
|
pendingClears.set(parent, [rect])
|
|
}
|
|
if (isAbsolute) {
|
|
absoluteNodeRemoved = true
|
|
}
|
|
}
|
|
|
|
export function consumeAbsoluteRemovedFlag(): boolean {
|
|
const had = absoluteNodeRemoved
|
|
absoluteNodeRemoved = false
|
|
return had
|
|
}
|