import { css, html } from "lit"; import { property } from "lit/decorators.js"; import { provide } from "@lit/context"; import { BaseElement } from "../../core/base-element.ts"; import { applyThemeToElement, type CTTheme, defaultTheme, mergeWithDefaultTheme, themeContext, } from "../theme-context.ts"; import { type Cell, isCell } from "@commontools/runner"; /** * ct-theme — Provides a theme to a subtree and applies CSS vars. * * Usage: * * * The component merges a partial theme (recipe-style) with defaults, * provides the result via context, and sets CSS custom properties on * the host so descendants pick up tokens. * * @element ct-theme */ export class CTThemeProvider extends BaseElement { static override styles = css` :host { display: contents; /* do not add extra layout */ } `; /** Partial or full theme object (recipe-style supported) */ @property({ attribute: false }) theme: any = {}; /** Computed full theme that is provided to children */ @provide({ context: themeContext }) @property({ attribute: false }) _computedTheme: CTTheme = defaultTheme; #unsubs: Array<() => void> = []; override firstUpdated(changed: Map) { super.firstUpdated(changed); this._recomputeAndApply(); } override updated(changed: Map) { super.updated(changed); if (changed.has("theme")) { this._recomputeAndApply(); } } private _recomputeAndApply() { this._computedTheme = mergeWithDefaultTheme(this.theme); applyThemeToElement(this, this._computedTheme); this.#setupSubscriptions(); } #setupSubscriptions() { // Clear previous subscriptions for (const off of this.#unsubs) off(); this.#unsubs = []; const t = this.theme as Record | undefined; if (!t) return; // Subscribe to top-level cell properties to refresh CSS vars on change for (const key of Object.keys(t)) { const val = (t as any)[key]; if (isCell && isCell(val)) { const cellVal = val as Cell; const off = cellVal.sink(() => this._recomputeAndApply()); this.#unsubs.push(off); } } } override disconnectedCallback(): void { super.disconnectedCallback(); for (const off of this.#unsubs) off(); this.#unsubs = []; } override render() { return html` `; } } customElements.define("ct-theme", CTThemeProvider);