nevera
Version:
Minimal 3kb charts as web components
90 lines (78 loc) • 2.21 kB
text/typescript
import { html, svg, render } from "lit-html";
import { scaleBand, scaleLinear, attachEvents } from "../utils.js";
type Bars = {
x: number;
y: number;
width: number;
height: number;
fill?: string;
}[];
type Options = { width: number; height: number };
const getSvgTemplate = (bars: Bars, options: Options) => html`<svg
width="100%"
height="100%"
viewBox="0 0 ${options.width} ${options.height}"
xmlns="http://www.w3.org/2000/svg"
>
${bars.map(
(bar) =>
svg`<rect class="bar" x="${bar.x}" y="${bar.y}" width="${bar.width}" height="${bar.height}"
style="fill: var(--nev-fill); stroke: var(--nev-stroke); stroke-width: var(--nev-stroke-width); fill: ${bar.fill}">
</rect>`
)}
</svg>`;
type Data = Array<{
key: string;
value: number;
fill?: string;
}>;
const getChartData = (data: Data, options: Options) => {
const x = scaleBand(
data.map((d) => d.key),
[0, options.width],
0.1
);
const y = scaleLinear([0, Math.max(...data.map((d) => d.value))], [options.height, 0]);
const bars = data.map((d) => ({
x: x(d.key) || 0,
y: y(d.value),
fill: d.fill,
width: x?.bandWidth ?? 1,
height: options.height - y(d.value),
}));
return bars;
};
class NeveraBarchart extends HTMLElement {
static observedAttributes = ["height", "width"];
constructor() {
super();
}
_data?: Data = undefined;
set data(val: Data | undefined) {
this._data = val;
this.renderSvg();
}
get data() {
return this._data;
}
connectedCallback() {
this.renderSvg();
setTimeout(() => attachEvents(this.querySelector("svg")!, "rect", this.data!, this), 10);
}
renderSvg() {
if (this.data) {
const options = {
width: Number(this.getAttribute("width")) || 400,
height: Number(this.getAttribute("height")) || 200,
};
const bars = getChartData(this.data, options);
render(getSvgTemplate(bars, options), this);
}
}
attributeChangedCallback(oldValue: string | null, newValue: string | null) {
if (oldValue !== newValue) {
this.renderSvg();
}
}
}
if (!customElements.get("nev-barchart")) customElements.define("nev-barchart", NeveraBarchart);