JSFiddle - React, Tailwind, and code Playground
by stitot
HTML
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/navigator.js"></script>
<div id="container-total"></div>
<div id="navigator-container"></div>
CSS
.container {
height: 400px;
margin: 20px;
}
body {
font-family: sans-serif;
padding: 1rem;
}
label {
margin-right: 1rem;
}
#controls {
margin-bottom: 1rem;
}
#last-updated {
margin-top: 0.5rem;
font-size: 0.9rem;
color: #666;
}
div.tooltip div {
display: flex;
justify-content: space-between;
padding: 4px 0;
}
div.tooltip span:first-child {
flex: 1;
text-align: left;
}
div.tooltip span:last-child {
text-align: right;
min-width: 80px;
font-variant-numeric: tabular-nums;
}
JavaScript
const BASES = {
jsDelivr: "https://data.jsdelivr.com/v1/stats/packages/npm",
npmRange: "https://api.npmjs.org/downloads/range",
npmRegistry: "https://registry.npmjs.org",
};
const PACKAGE = "@highcharts/dashboards";
const EARLIEST = {
jsDelivr: new Date("2018-06-01"),
npm: new Date("2015-03-11"),
};
async function fetchDownloadStats() {
const fetchJson = async (url, label) => {
console.log(label + ": ", url);
const res = await fetch(url);
if (!res.ok) throw new Error(`${label} fetch failed`);
return res.json();
};
const fetchVersions = async () => {
const url = `${BASES.npmRegistry}/${encodeURIComponent(PACKAGE)}`;
const data = await fetchJson(url, "versions");
return Object.entries(data.time)
.filter(([k]) => k !== "created" && k !== "modified")
.reduce((acc, [ver, date]) => ({ ...acc, [ver]: date.slice(0, 10) }), {});
};
const versions = await fetchVersions();
const versionDates = Object.values(versions).sort();
const versionStart = new Date(versionDates[0]);
const fromDateJs = new Date(Math.max(versionStart, EARLIEST.jsDelivr));
const fromDateNpm = new Date(Math.max(versionStart, EARLIEST.npm));
const toDate = new Date();
toDate.setDate(toDate.getDate() - 2); // safety buffer
const fetchStats = async (source, fromDate) => {
const result = [];
if (source === "jsdelivr") {
for (let y = fromDate.getFullYear(); y <= toDate.getFullYear(); y++) {
const period = y === new Date().getFullYear() ? "year" : y.toString();
const url = `${BASES.jsDelivr}/${PACKAGE}?period=${period}`;
const json = await fetchJson(url, "jsDelivr");
for (const [day, hits] of Object.entries(json.hits?.dates || {})) {
const d = new Date(day);
if (d >= fromDate && d <= toDate) result.push([day, hits]);
}
}
}
if (source === "npm") {
let start = new Date(fromDate);
...