1+ import { h } from 'https://esm.sh/preact' ;
2+
3+ function getPlatform ( assetName ) {
4+ assetName = assetName . toLowerCase ( ) ;
5+ if ( assetName . includes ( 'msi' ) ) return 'Windows' ;
6+ if ( assetName . includes ( 'arm64.dmg' ) ) return 'Mac Silicon' ;
7+ if ( assetName . includes ( 'i686.dmg' ) || assetName . includes ( '.dmg' ) ) return 'Mac Intel' ;
8+ if ( assetName . includes ( 'container' ) ) return 'Container' ;
9+ return 'Other' ;
10+ }
11+
12+ function getColorForPlatform ( platform ) {
13+ switch ( platform ) {
14+ case 'Windows' : return '#00A4EF' ;
15+ case 'Mac Intel' : return '#A2AAAD' ;
16+ case 'Mac Silicon' : return '#C4C4C4' ;
17+ case 'Container' : return '#FFD700' ;
18+ default : return '#FF69B4' ;
19+ }
20+ }
21+
22+ export function YearlyChart ( { stats } ) {
23+ if ( ! stats || ! stats . releases || ! Array . isArray ( stats . releases ) ) {
24+ return h ( 'div' , { class : 'charts' } , 'No data available' ) ;
25+ }
26+
27+ const platforms = [ 'Windows' , 'Mac Intel' , 'Mac Silicon' , 'Container' , 'Other' ] ;
28+
29+ // Group downloads by year
30+ const yearlyDownloads = stats . releases . reduce ( ( acc , release ) => {
31+ const year = new Date ( release . published_at ) . getFullYear ( ) ;
32+
33+ if ( ! acc [ year ] ) {
34+ acc [ year ] = platforms . reduce ( ( platformAcc , platform ) => {
35+ platformAcc [ platform ] = 0 ;
36+ return platformAcc ;
37+ } , { total : 0 } ) ;
38+ }
39+
40+ release . assets . forEach ( asset => {
41+ const platform = getPlatform ( asset . name ) ;
42+ acc [ year ] [ platform ] += asset . download_count || 0 ;
43+ acc [ year ] . total += asset . download_count || 0 ;
44+ } ) ;
45+
46+ return acc ;
47+ } , { } ) ;
48+
49+ // Convert to array and sort by year (descending)
50+ const yearlyStats = Object . entries ( yearlyDownloads )
51+ . map ( ( [ year , stats ] ) => ( {
52+ year : parseInt ( year ) ,
53+ platforms : stats ,
54+ total : stats . total
55+ } ) )
56+ . sort ( ( a , b ) => b . year - a . year ) ;
57+
58+ const maxDownloads = Math . max ( ...yearlyStats . map ( y => y . total ) ) ;
59+
60+ return h ( 'div' , { class : 'charts' } , [
61+ h ( 'div' , { class : 'chart horizontal' } , [
62+ h ( 'div' , { class : 'chart-legend' } ,
63+ platforms . map ( platform =>
64+ h ( 'div' , { class : 'legend-item' } , [
65+ h ( 'span' , {
66+ class : 'legend-color' ,
67+ style : `background-color: ${ getColorForPlatform ( platform ) } `
68+ } ) ,
69+ h ( 'span' , { class : 'legend-label' } , platform )
70+ ] )
71+ )
72+ ) ,
73+ h ( 'div' , { class : 'chart-container horizontal' } ,
74+ yearlyStats . map ( yearStat =>
75+ h ( 'div' , { class : 'bar-group horizontal' } , [
76+ h ( 'div' , { class : 'bar-label horizontal' } , [
77+ h ( 'span' , { class : 'version-label' } , yearStat . year )
78+ ] ) ,
79+ h ( 'div' , { class : 'stacked-bars horizontal' } ,
80+ platforms . map ( platform => {
81+ const width = ( yearStat . platforms [ platform ] / maxDownloads ) * 400 ;
82+ return h ( 'div' , {
83+ class : 'bar stacked-bar horizontal' ,
84+ style : `
85+ width: ${ width } px;
86+ background-color: ${ getColorForPlatform ( platform ) } ;
87+ ` ,
88+ title : `${ platform } : ${ yearStat . platforms [ platform ] . toLocaleString ( ) } downloads`
89+ } ) ;
90+ } )
91+ ) ,
92+ h ( 'div' , { class : 'total-downloads' } ,
93+ `${ yearStat . total . toLocaleString ( ) } downloads`
94+ )
95+ ] )
96+ )
97+ )
98+ ] )
99+ ] ) ;
100+ }
0 commit comments