# @awaitConnectedAncestors and @dispatchConnectedEvent

The `@awaitConnectedAncestors` and `@dispatchConnectedEvent` decorators delay a web component's initialization until its matching ancestors have executed their `connectedCallback`. This is when contextual elements (publisher, dataProvider, etc.) are configured.

## Principle

When a child component attaches to the DOM, its ancestors may not yet be initialized (especially if custom element definitions are loaded asynchronously). The `@awaitConnectedAncestors` decorator delays the component's `connectedCallback` until all ancestors matching the provided CSS selectors have executed their `connectedCallback`.

The `@dispatchConnectedEvent` decorator allows ancestors to signal they are ready by dispatching the `sonic-connected` event at the end of their `connectedCallback`. The event bubbles, so it can be listened to from anywhere (e.g. `document.addEventListener(CONNECTED, handler)`).

Ancestors that are not web components (no hyphen in tag name) are considered connected by default and do not need to emit the event.

## Usage

### Import

<sonic-code language="typescript">
  <template>
import { awaitConnectedAncestors, dispatchConnectedEvent, ancestorAttribute } from "@supersoniks/concorde/decorators";
  </template>
</sonic-code>

### Basic example

An ancestor container decorated with `@dispatchConnectedEvent()` signals when it is ready. A child component decorated with `@awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]")` waits for this container to be initialized before initializing itself. Parameters are CSS selectors (`element.matches()`).

The parent is registered via `customElements.define()` (vanilla JS) rather than `@customElement`, so it can be defined later—e.g. when the user clicks a button. This demonstrates the child waiting until the parent exists.

<sonic-code language="typescript">
  <template>
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
//
// Parent: registered later via customElements.define(), not @customElement
@dispatchConnectedEvent()
export class DemoWaitAncestorContainer extends LitElement {
  render() {
    return html`<slot></slot>`;
  }
}
//
// Child: waits for parent before initializing
@customElement("demo-wait-ancestor-value")
@awaitConnectedAncestors("demo-wait-ancestor-container[dataProvider]")
export class DemoWaitAncestorValue extends LitElement {
  @ancestorAttribute("dataProvider")
  dataProvider: string | null = null;
  //
  @state() initializedAt: string = "";
  //
  connectedCallback() {
    super.connectedCallback();
    this.initializedAt = new Date().toISOString();
  }
  //
  render() {
    return html`
      <p>DataProvider from ancestor: <strong>${this.dataProvider || "—"}</strong></p>
      <p>Initialized at: ${this.initializedAt || "(waiting for parent…)"}</p>
    `;
  }
}
//
// Demo section: register parent via customElements.define() when user clicks
@customElement("demo-wait-ancestors-section")
export class DemoWaitAncestorsSection extends LitElement {
  registerParent() {
    if (!customElements.get("demo-wait-ancestor-container")) {
      customElements.define("demo-wait-ancestor-container", DemoWaitAncestorContainer);
    }
  }
  render() {
    return html`
      <sonic-button @click=${this.registerParent}>Register parent component</sonic-button>
      <demo-wait-ancestor-container dataProvider="waitAncestorDemo">
        <demo-wait-ancestor-value></demo-wait-ancestor-value>
      </demo-wait-ancestor-container>
    `;
  }
}
  </template>
</sonic-code>

<sonic-code>
  <template>
<demo-wait-ancestors-section></demo-wait-ancestors-section>
  </template>
</sonic-code>

### Multiple ancestors

The child waits for all specified ancestors. Register outer first, then inner — the child initializes only when both are ready.

<sonic-code>
  <template>
<demo-wait-ancestors-multi-section></demo-wait-ancestors-multi-section>
  </template>
</sonic-code>

### Ancestors already connected

When the parent is defined at load and already in the DOM, the child initializes immediately (no delay).

**Static (both in DOM from start):**

<sonic-code>
  <template>
<demo-wait-ancestors-static-section></demo-wait-ancestors-static-section>
  </template>
</sonic-code>

**Dynamic (child added on button click):**

<sonic-code>
  <template>
<demo-wait-ancestors-ready-section></demo-wait-ancestors-ready-section>
  </template>
</sonic-code>

## CSS selector support

Parameters are CSS selectors matched via `element.matches()` — e.g. `"sonic-subscriber"`, `"sonic-subscriber[dataProvider]"`, `".my-container"`, or multiple: `"sonic-subscriber", "sonic-sdui"`.

## Behavior

- Ancestor search uses CSS selectors (`element.matches(selector)`) — supports tag names, classes, attributes, combinators, etc.
- Traversal includes shadow roots (parentNode / host)
- Non-web components (no hyphen in tag name) are considered connected by default
- For web component ancestors, it waits for `customElements.whenDefined(tagName)` and the `sonic-connected` event (or a timeout as fallback)
- If no matching ancestor is found, the original `connectedCallback` is called immediately
- Ancestors that do not emit `sonic-connected` trigger the fallback after the timeout (compatibility with existing components)

## Use cases

These decorators are particularly useful for:

- **sonic-value** inside a **sonic-subscriber**: the value waits for the subscriber to configure its publisher
- **Components inside sonic-sdui**: wait for the SDUI to load and configure its context
- **Any component** depending on context provided by an ancestor custom element

## Listening to the connected event

The `sonic-connected` event bubbles, so you can listen to it from anywhere:

<sonic-code language="typescript">
  <template>
import { CONNECTED } from "@supersoniks/concorde/decorators";
//
someConnectable.addEventListener(CONNECTED, (e) => {
  console.log("Component connected:", e.target);
});
  </template>
</sonic-code>

## Notes

- These decorators apply only to web components (classes extending `HTMLElement`)
- The fallback timeout ensures compatibility with components that do not yet use `@dispatchConnectedEvent`
- Traversal includes shadow roots
- For a guarantee without relying on the timeout, decorate ancestors (Subscriber, Fetcher, etc.) with `@dispatchConnectedEvent`
