Skip to content

Instantly share code, notes, and snippets.

@iliakan
Created December 23, 2019 17:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iliakan/064ef83dcedbc97cb4b203a8b23f7d53 to your computer and use it in GitHub Desktop.
Save iliakan/064ef83dcedbc97cb4b203a8b23f7d53 to your computer and use it in GitHub Desktop.
<!doctype html>
<body>
<link rel="stylesheet" href="../../styles/common.css">
<link rel="stylesheet" href="style.css">
<script type="module">
import salesData from './salesData.js';
import ColumnChart from './index.js';
let chart = new ColumnChart({
url: '/api/dashboard/orders',
height: 200,
range: {
from: new Date(Date.now() - 60*86400e3),
to: new Date()
},
name: 'orders',
title: "Orders",
link: '/orders',
formatHeading: data => Object.values(data).reduce((a, b) => a + b, 0),
formatTooltip: (date, value) => `${date}: ${value}`
});
document.body.appendChild(chart.elem);
</script>
</body>
import fetchJson from "../../lib/fetchJson.js";
import createElement from "../../lib/createElement.js";
export default class ColumnChart {
/**
* @param data object {key: value}
* @param height fixed height of the widget
* @param name unique name, appended to the class
* @param title
* @param link
* @param formatHeading function to format the header
* @param formatTooltip function to format the tooltip
*/
constructor({ url, range, height, name, title, link, formatHeading, formatTooltip }) {
this.url = new URL(url, location.href);
this.range = range;
this.height = height;
this.name = name;
this.title = title;
this.link = link;
this.formatHeading = formatHeading;
this.formatTooltip = formatTooltip;
this.render();
}
async render() {
this.elem = createElement(`<div class="column-chart dashboard__chart_${this.name}">
<div class="column-chart__title">
${this.title}
${
this.link ? `<a href="${this.link}" class="column-chart__link">View all</a>` : ``
}
</div>
<div class="column-chart__container">
<div class="column-chart__header"></div>
<div class="column-chart__chart"></div>
</div>
</div>`);
this.chartContainer = this.elem.querySelector('.column-chart__container');
this.chartElem = this.elem.querySelector('.column-chart__chart');
this.chartElem.addEventListener('mouseover', e => this.onChartMouseOver(e));
this.chartElem.addEventListener('mouseout', e => this.onChartMouseOut(e));
await this.update();
}
onChartMouseOver(event) {
if (event.target.parentNode !== this.chartElem) {
return; // click not on a column
}
let column = event.target;
// not using :hover, as it's not supported on mobiles
this.chartElem.classList.add('has-hovered');
column.classList.add('is-hovered');
}
onChartMouseOut(event) {
if (this.chartElem.classList.contains('has-hovered')) {
this.chartElem.classList.remove('has-hovered');
this.chartElem.querySelector('.is-hovered').classList.remove('is-hovered');
}
}
setRange(range) {
this.range = range;
this.update();
}
async update() {
// clear previous data if any
this.chartElem.innerHTML = "";
this.elem.classList.add(`column-chart_loading`);
this.url.searchParams.set('from', this.range.from.toISOString());
this.url.searchParams.set('to', this.range.to.toISOString());
let chartData = await fetchJson(this.url);
this.elem.classList.remove(`column-chart_loading`);
let maxValue = Math.max(...Object.values(chartData));
let scale = getComputedStyle(this.chartElem).getPropertyValue('--chart-height') / maxValue;
let chartColumnsHtml = ``;
for(let [date, value] of Object.entries(chartData)) {
// console.log(this.height, Math.floor(value*scale));
chartColumnsHtml += `<div style="--value:${Math.floor(value*scale)}" data-tooltip="${this.formatTooltip(new Date(date), value)}"></div>`
}
this.elem.querySelector('.column-chart__header').innerHTML = this.formatHeading(chartData);
this.chartElem.innerHTML = chartColumnsHtml;
}
};
export default {
"2019-08-20": 163062,
"2019-08-21": 61193,
"2019-08-22": 79611,
"2019-08-23": 61484,
"2019-08-24": 36438,
"2019-08-25": 108245,
"2019-08-26": 172881,
"2019-08-27": 183783,
"2019-08-28": 243141,
"2019-08-29": 213068,
"2019-08-30": 213854,
"2019-08-31": 207498,
"2019-09-01": 151681,
"2019-09-02": 90608,
"2019-09-03": 195460,
"2019-09-04": 282072,
"2019-09-05": 251894,
"2019-09-06": 132690,
"2019-09-07": 158875,
"2019-09-08": 193952,
"2019-09-09": 187590,
"2019-09-10": 126407,
"2019-09-11": 189518,
"2019-09-12": 145015,
"2019-09-13": 259758,
"2019-09-14": 184997,
"2019-09-15": 103850,
"2019-09-16": 180280,
"2019-09-17": 218248,
"2019-09-18": 206180,
"2019-09-19": 170767,
"2019-09-20": 252596,
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style rel="stylesheet">
body {
background: var(--light-blue); padding: 100px;
}
</style>
<link rel="stylesheet" href="../../styles/common.css">
<link href="style.css">
</head>
<body>
</body>
</html>
.column-chart__chart {
display: grid;
grid-column-gap: 1px;
grid-auto-columns: 1fr;
grid-template-rows: repeat(50, minmax(0, 1fr));
width: 100%;
height: calc(var(--chart-height) * 1px);
}
.column-chart__chart div {
--start: calc(var(--chart-height) + 1 - var(--value));
grid-row: var(--start) / -1;
background-color: var(--chart-column-color);
min-width: 1px;
cursor: pointer;
}
.column-chart__chart div.is-hovered {
opacity: 1;
}
.column-chart__chart.has-hovered div:not(.is-hovered) {
opacity: 0.5;
}
.column-chart__container {
max-width: 100%;
position: relative;
}
.column-chart {
padding: 16px 26px 24px;
background: var(--white);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
border-radius: 4px;
border-left: 2px solid var(--chart-column-color);
position: relative;
}
.column-chart__title {
font-size: 16px;
line-height: 20px;
color: var(--grey);
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 8px;
}
.column-chart__link {
color: var(--blue);
text-decoration: none;
}
.column-chart__header {
font-weight: 600;
font-size: 28px;
line-height: 35px;
color: var(--dark-blue);
margin-bottom: 28px;
position: relative;
}
.column-chart_loading .column-chart__header,
.column-chart_loading .column-chart__chart {
display: none;
}
.column-chart_loading .column-chart__container:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 113px;
background: url("/assets/images/charts-skeleton.svg") center no-repeat;
background-size: cover;
display: block;
}
.column-chart_loading .loading-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment