JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
<body class="bg-gray-200" x-data="downloads" x-init="init()">
<div class="p-8 bg-gray-200" x-ref="details">
<div class="bg-white rounded-lg shadow-lg">
<div class="p-6 flex items-center gap-4">
<svg xmlns="http://www.w3.org/2000/svg" class="w-12" viewBox="0 0 512 512" xml:space="preserve">
<path fill="#cff09e" d="M319.666 253.182V95.093H192.334v158.089h-60.592L256 416.907l124.258-163.725z" />
<g fill="#507c5c">
<path d="M256 431.967a15.063 15.063 0 0 1-11.996-5.954L119.748 262.287a15.06 15.06 0 0 1 11.996-24.164h45.533V95.092c0-8.315 6.742-15.059 15.059-15.059h127.331c8.317 0 15.059 6.743 15.059 15.059v143.03h45.533a15.06 15.06 0 0 1 11.996 24.164L267.997 426.011A15.062 15.062 0 0 1 256 431.967zm-93.925-163.726L256 391.999l93.925-123.758h-30.259c-8.317 0-15.059-6.743-15.059-15.059V110.151h-97.214v143.03c0 8.315-6.742 15.059-15.059 15.059h-30.259v.001z" />
<path d="M256 512C114.842 512 0 397.158 0 256S114.842 0 256 0c8.317 0 15.059 6.743 15.059 15.059S264.317 30.118 256 30.118C131.448 30.118 30.118 131.448 30.118 256S131.448 481.882 256 481.882 481.882 380.552 481.882 256c0-68.911-30.874-133.183-84.706-176.34-6.489-5.203-7.532-14.681-2.33-21.17 5.203-6.49 14.679-7.529 21.168-2.331C477.015 105.064 512 177.903 512 256c0 141.158-114.842 256-256 256z" />
</g>
</svg>
<h2 class="text-3xl uppercase text-green-800 tracking-wide">Downloads</h2>
</div>
<div class="px-4 pb-6">
<template x-for="pacakge in packages">
<div class="flex items-center rounded-lg p-2 cursor-pointer hover:bg-gray-100">
<div class="w-10 h-10 rounded-full bg-green-100 p-2">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512.001 512.001" xml:space="preserve">
<path d="M86.883...
JavaScript
var downloads = {
vendor: 'tzsk',
packages: [],
total: 0,
exportUrl: '',
async init() {
const {
packageNames
} = await getAllPackages(this.vendor);
const packages = packageNames.map(name => ({
name,
count: 0,
total: 0,
}));
this.packages = packages;
this.fetchAll();
},
async fetchAll() {
const calls = this.packages.map(async ({
name
}) => {
const {
package
} = await getDetailByPackageName(name);
return {
name,
count: this.formatNumber(package.downloads.total),
total: package.downloads.total
};
});
const results = await Promise.all(calls);
this.packages = results;
const total = this.packages.reduce((prev, curr) => prev + curr.total, 0);
this.total = this.formatNumber(total);
setTimeout(() => {
this.readyToExport();
}, 400);
},
async readyToExport() {
const blob = await domtoimage.toBlob(this.$refs.details);
this.exportUrl = URL.createObjectURL(blob);
},
formatNumber(number) {
return new Intl.NumberFormat('en-US').format(number);
},
exportImage() {
const link = document.createElement('a');
link.href = this.exportUrl;
link.setAttribute('download', `${this.vendor}.png`);
link.click();
}
};
function getAllPackages(vendor) {
return fetch(`https://packagist.org/packages/list.json?vendor=${vendor}`)
.then(res => res.json());
}
function getDetailByPackageName(name) {
return fetch(`https://packagist.org/packages/${name}.json`)
.then(res => res.json());
}