blob: 04f008715066fe2097241e302e6eb7962d1d6beb [file] [log] [blame]
Sean McCullough86b56862025-04-18 13:04:03 -07001import { css, html, LitElement } from "lit";
2import { customElement, property, query } from "lit/decorators.js";
3import vegaEmbed from "vega-embed";
4import { VisualizationSpec } from "vega-embed";
5
6/**
7 * A web component wrapper for vega-embed.
8 * Renders Vega and Vega-Lite visualizations.
9 *
10 * Usage:
11 * <vega-embed .spec="${yourVegaLiteSpec}"></vega-embed>
12 */
13@customElement("vega-embed")
14export class VegaEmbed extends LitElement {
15 /**
16 * The Vega or Vega-Lite specification to render
17 */
18 @property({ type: Object })
19 spec?: VisualizationSpec;
20
21 static styles = css`
22 :host {
23 display: block;
24 width: 100%;
25 height: 100%;
26 }
27
28 #vega-container {
29 width: 100%;
30 height: 100%;
31 min-height: 200px;
32 }
33 `;
34
35 @query("#vega-container")
36 protected container?: HTMLElement;
37
38 protected firstUpdated() {
39 this.renderVegaVisualization();
40 }
41
42 protected updated() {
43 this.renderVegaVisualization();
44 }
45
46 /**
47 * Renders the Vega/Vega-Lite visualization using vega-embed
48 */
49 private async renderVegaVisualization() {
50 if (!this.spec) {
51 return;
52 }
53
54 if (!this.container) {
55 return;
56 }
57
58 try {
59 // Clear previous visualization if any
60 this.container.innerHTML = "";
61
62 // Render new visualization
63 await vegaEmbed(this.container, this.spec, {
64 actions: true,
65 renderer: "svg",
66 });
67 } catch (error) {
68 console.error("Error rendering Vega visualization:", error);
69 this.container.innerHTML = `<div style="color: red; padding: 10px;">
70 Error rendering visualization: ${
71 error instanceof Error ? error.message : String(error)
72 }
73 </div>`;
74 }
75 }
76
77 render() {
78 return html`<div id="vega-container"></div> `;
79 }
80}
81
82declare global {
83 interface HTMLElementTagNameMap {
84 "vega-embed": VegaEmbed;
85 }
86}