import { css, html } from "lit"; import { property } from "lit/decorators.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { BaseElement } from "../../core/base-element.ts"; import { type CellHandle, isCellHandle } from "@commontools/runtime-client"; import { sanitizeSvg } from "./sanitize-svg.ts"; /** * CTSvg - Renders SVG content from a string * * @element ct-svg * * @attr {string} content - The SVG markup to render (string or CellHandle) * * @csspart content - The SVG content wrapper * * @example * * * @example * */ export class CTSvg extends BaseElement { static override styles = [ BaseElement.baseStyles, css` :host { display: block; box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; } .svg-content { width: 100%; height: auto; } .svg-content svg { width: 100%; height: auto; display: block; } `, ]; @property({ attribute: false }) declare content: CellHandle | string; private _unsubscribe: (() => void) | null = null; constructor() { super(); this.content = ""; } private _getContentValue(): string { if (isCellHandle(this.content)) { return this.content.get() ?? ""; } return this.content ?? ""; } override willUpdate( changedProperties: Map, ) { super.willUpdate(changedProperties); // Handle Cell subscription before render so first render has correct value if (changedProperties.has("content")) { // Clean up previous subscription if (this._unsubscribe) { this._unsubscribe(); this._unsubscribe = null; } // Subscribe to new Cell if it's a Cell if (this.content && isCellHandle(this.content)) { this._unsubscribe = this.content.subscribe(() => { this.requestUpdate(); }); } } } override disconnectedCallback() { super.disconnectedCallback(); if (this._unsubscribe) { this._unsubscribe(); this._unsubscribe = null; } } override render() { const contentValue = this._getContentValue(); return html`
${unsafeHTML(sanitizeSvg(contentValue))}
`; } } globalThis.customElements.define("ct-svg", CTSvg);