package vdom // Ref is a handle on the real DOM node a VNode rendered to. It is the bridge the // neutral component code uses to ask the browser for things it cannot know on its // own — how big an element is, where it sits in the viewport — without importing // anything platform-specific: the value inside is opaque (`any`), filled in by the // wasm reconciler and always nil on the server. Read it through the wasmruntime // host API (wasmruntime.Rect, SetStyle, Focus, …), which no-ops when it is nil. // // A component creates one ref per element it needs to measure and keeps it across // renders (in a closure, next to its signals — NOT rebuilt inside the render // function, or it would be a fresh empty ref every frame): // // panel := vdom.NewRef() // return func() *VNode { // return Div(WithRef(panel), Attr("class", "…"), …) // } // // and later, after the DOM exists (see wasmruntime.AfterRender): // // r := wasmruntime.Rect(panel) // zero Rect if unmounted or on the server type Ref struct{ node any } // NewRef returns an unattached Ref. func NewRef() *Ref { return &Ref{} } // Node is the platform DOM handle (a js.Value in the browser), or nil if the ref // is not currently attached to a mounted element. Component code should not need // this — it is for the host API and the reconciler. func (r *Ref) Node() any { if r == nil { return nil } return r.node } // Mounted reports whether the ref currently points at a live DOM node. func (r *Ref) Mounted() bool { return r != nil && r.node != nil } type refMod struct{ ref *Ref } func (m refMod) apply(n *VNode) { n.Ref = m.ref } // WithRef attaches a Ref to this element, so the DOM node it renders to can be // measured and manipulated. Ignored on the server. func WithRef(r *Ref) Mod { return refMod{r} } // SetRefNode points a Ref at a platform DOM handle (or nil to detach). It exists // for the reconciler and the host API, which live in another package; component // code has no reason to call it. func SetRefNode(r *Ref, node any) { if r != nil { r.node = node } }