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
	}
}
