UNPKG

@firstprateek/bar-chart

Version:

A customizable web component that plots a bar chart with ordinal values in the x axis and numerical values in y axis.

204 lines (174 loc) 6.57 kB
import {LitElement, html, css} from 'lit'; import {styleMap} from 'lit/directives/style-map.js'; /** * Bar Chart: <bar-chart></bar-chart>. * * A widget that renders a bar chart with ordinal values in the x-axis and numerical values in y-axis. * * @property width - Number. width of the widget in pixels, default value is 460 * @property height - Number. height of the widget in pixels, default value is 400 * @property data - Array. An array of objects. Each object should be of the form { "group": <group_str>, "value": <number> }, default value is [] * @property min - Number. Min range value for the y-axis. Default is 0 * @property max - Number. Max range value for the y-axis. Default is the max "value" in the data stream * @property transitionDuration - Number. Time taken to transition from one data value to another in ms. Default is 1000 * @property dataApi - String. URL for retrieving the data * @property updateFrequency - Number. Seconds after which the chart data should be auto-updated, default is 60 seconds * @property autoUpdate - Boolean. Seconds after which the chart data should be auto-updated, default is 60 seconds * * @fires updated - Indicates when the chart was auto-updated * * @cssproperty --bar-chart-background-color - background color of the poll, default is white * @cssproperty --bar-chart-bar-color - background color for the individual bars, default is #69B3A2 * @cssproperty --bar-chart-axis-color - color of the axis, default is black * @cssproperty --bar-chart-text-color - color of the axis text, default is black */ export default class BarChart extends LitElement { static styles = css` :host { --background-color: var(--bar-chart-background-color, white); --bar-color: var(--bar-chart-bar-color, #69B3A2); --axis-color: var(--bar-chart-axis-color, black); --text-color: var(--bar-chart-text-color, black); } `; static properties = { width: {type: Number}, height: {type: Number}, data: {type: Array}, min: {type: Number}, max: {type: Number}, transitionDuration: {type: Number}, dataAPI: {}, updateFrequency: {type: Number}, autoUpdate: {type: Boolean} } constructor() { super(); // Reactive properties this.width = 460; this.height = 400; this.data = []; this.min = 0; this.max = 20; this.transitionDuration = 1000; this.dataAPI = ""; this.updateFrequency = 60; this.autoUpdate = false; // Non-Reactive properties this._data1 = [ {group: "A", value: 4}, {group: "B", value: 16}, {group: "C", value: 8} ]; this._data2 = [ {group: "A", value: 7}, {group: "B", value: 1}, {group: "C", value: 20} ]; this._SVG = null; this._x = null; this._y = null; this._width = null; this._height = null; } async connectedCallback() { super.connectedCallback(); if (this.dataAPI) { await this.fetchResults(); if (this.autoUpdate) { this.setupUpdateTimer(); } } } disconnectedCallback() { super.disconnectedCallback(); if (this.autoUpdate) { this.removeUpdateTimer(); } } setupUpdateTimer() { if (!this._timer) { this._timer = setInterval(() => this.fetchResults(), this.updateFrequency * 1000); } } removeUpdateTimer() { clearInterval(this._timer); delete this._timer; } async fetchResults() { const response = await fetch(this.dataAPI); const result = await response.json(); this.updateView(result); } updateView(result) { this.max = result.max; this.min = result.min; this.data = result.data; const options = { detail: { ...result }, bubbles: true, composed: true } this.dispatchEvent(new CustomEvent('updated', options)); } updated(changedProperties) { // Set the dimensions and margins of the graph const margin = {top: 30, right: 30, bottom: 70, left: 60}; this._width = this.width - margin.left - margin.right; this._height = this.height - margin.top - margin.bottom; this._data1 = this.data; // Append the SVG object to the body of the page if (!this._SVG) { this._SVG = d3.select(this.containerDiv) .append("svg") .attr("width", this._width + margin.left + margin.right) .attr("height", this._height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); } this._SVG.selectAll('g').remove(); // X axis this._x = d3.scaleBand() .range([ 0, this._width ]) .domain(this._data1.map(d => d.group)) .padding(0.2); this._SVG.append("g") .attr("transform", `translate(0,${this._height})`) .call(d3.axisBottom(this._x)) // Add Y axis this._y = d3.scaleLinear() .domain([this.min, this.max]) .range([ this._height, 0]); this._SVG.append("g") .attr("class", "myYaxis") .call(d3.axisLeft(this._y)); // Initialize the plot with the first dataset this.updateChart(this._data1) } get containerDiv() { return this.renderRoot?.querySelector('#my_dataviz') ?? null; } render() { const styles = styleMap({ width: `${this.width}px`, height: `${this.height}px` }); return html` <div style=${styles} id="my_dataviz"></div> `; } updateChart(data) { // A function that create / update the plot for a given variable: var u = this._SVG.selectAll("rect") .data(data) u .join("rect") .transition() .duration(this.transitionDuration) .attr("x", d => this._x(d.group)) .attr("y", d => this._y(d.value)) .attr("width", this._x.bandwidth()) .attr("height", d => this._height - this._y(d.value)) .attr("fill", "#69b3a2") } } customElements.define('bar-chart', BarChart);