@@ -29,6 +29,7 @@ String squareColorClass(int i) => 'downloads-chart-square-${colors[i]}';
2929enum DisplayMode {
3030 stacked,
3131 unstacked,
32+ percentage,
3233}
3334
3435void create (HTMLElement element, Map <String , String > options) {
@@ -64,6 +65,8 @@ void create(HTMLElement element, Map<String, String> options) {
6465 .fuse (json.decoder)
6566 .convert (base64Decode (dataPoints)) as Map <String , dynamic >));
6667 final weeksToDisplay = math.min (40 , data.totalWeeklyDownloads.length);
68+ final totals =
69+ data.totalWeeklyDownloads.sublist (0 , weeksToDisplay).reversed.toList ();
6770
6871 final majorDisplayLists = prepareWeekLists (
6972 data.totalWeeklyDownloads,
@@ -106,14 +109,21 @@ void create(HTMLElement element, Map<String, String> options) {
106109 svg = createNewSvg ();
107110 element.append (svg);
108111 currentDisplayList = displayList;
109- drawChart (svg, toolTip, displayList, data.newestDate,
110- displayMode: currentDisplayMode);
112+ drawChart (
113+ svg,
114+ toolTip,
115+ displayList,
116+ data.newestDate,
117+ totals,
118+ displayMode: currentDisplayMode,
119+ );
111120 });
112121 });
113122
114123 final displayModesMap = < String , DisplayMode > {
115124 'stacked' : DisplayMode .stacked,
116- 'unstacked' : DisplayMode .unstacked
125+ 'unstacked' : DisplayMode .unstacked,
126+ 'percentage' : DisplayMode .percentage,
117127 };
118128
119129 final displayModes = document.getElementsByName (displayRadio).toList ();
@@ -131,19 +141,32 @@ void create(HTMLElement element, Map<String, String> options) {
131141 svg = createNewSvg ();
132142 element.append (svg);
133143 currentDisplayMode = displayMode;
134- drawChart (svg, toolTip, currentDisplayList, data.newestDate,
135- displayMode: displayMode);
144+ drawChart (
145+ svg,
146+ toolTip,
147+ currentDisplayList,
148+ data.newestDate,
149+ totals,
150+ displayMode: displayMode,
151+ );
136152 });
137153 });
138154
139- drawChart (svg, toolTip, majorDisplayLists, data.newestDate);
155+ drawChart (
156+ svg,
157+ toolTip,
158+ majorDisplayLists,
159+ data.newestDate,
160+ totals,
161+ );
140162}
141163
142164void drawChart (
143165 Element svg,
144166 HTMLDivElement toolTip,
145167 ({List <String > ranges, List <List <int >> weekLists}) displayLists,
146168 DateTime newestDate,
169+ List <int > totals,
147170 {DisplayMode displayMode = DisplayMode .unstacked}) {
148171 final ranges = displayLists.ranges;
149172 final values = displayLists.weekLists;
@@ -169,10 +192,13 @@ void drawChart(
169192 /// Computes max value on y-axis such that we get a nice division for the
170193 /// interval length between the numbers shown by the ticks on the y axis.
171194 (int maxY, int interval) computeMaxYAndInterval (List <List <int >> values) {
172- final maxDownloads = displayMode == DisplayMode .unstacked
173- ? values.fold <int >(1 , (a, b) => math.max <int >(a, b.reduce (math.max)))
174- : values.fold <int >(
175- 1 , (a, b) => math.max <int >(a, b.reduce ((x, y) => x + y)));
195+ final maxDownloads = switch (displayMode) {
196+ DisplayMode .unstacked =>
197+ values.fold <int >(1 , (a, b) => math.max <int >(a, b.reduce (math.max))),
198+ DisplayMode .stacked => values.fold <int >(
199+ 1 , (a, b) => math.max <int >(a, b.reduce ((x, y) => x + y))),
200+ _ => 100 // percentage
201+ };
176202
177203 final digits = maxDownloads.toString ().length;
178204 final buffer = StringBuffer ()..write ('1' );
@@ -195,7 +221,7 @@ void drawChart(
195221 final firstDate = computeDateForWeekNumber (newestDate, values.length, 0 );
196222 final xAxisSpan = newestDate.difference (firstDate);
197223
198- (double , double ) computeCoordinates (DateTime date, int downloads) {
224+ (double , double ) computeCoordinates (DateTime date, num downloads) {
199225 final duration = date.difference (firstDate);
200226 // We don't risk division by 0 here, since `xAxisSpan` is a non-zero duration.
201227 final x = leftPadding +
@@ -257,8 +283,10 @@ void drawChart(
257283 final tickLabel = SVGTextElement ();
258284 tickLabel.setAttribute (
259285 'class' , 'downloads-chart-tick-label downloads-chart-tick-label-y' );
260- tickLabel.text =
261- '${compactFormat (i * interval ).value }${compactFormat (i * interval ).suffix }' ;
286+ final suffix = displayMode == DisplayMode .percentage
287+ ? '%'
288+ : compactFormat (i * interval).suffix;
289+ tickLabel.text = '${compactFormat (i * interval ).value }$suffix ' ;
262290 tickLabel.setAttribute ('x' , '${xMax + marginPadding }' );
263291 tickLabel.setAttribute ('y' , '$y ' );
264292 chart.append (tickLabel);
@@ -288,19 +316,24 @@ void drawChart(
288316
289317 // Chart lines and legends
290318
291- final lastestDownloads = List .filled (values.length, 0 );
319+ final latestDownloads = List < num > .filled (values.length, 0 );
292320 final lines = < List <(double , double )>> [];
293321 for (int versionRange = 0 ; versionRange < values[0 ].length; versionRange++ ) {
294322 final List <(double , double )> lineCoordinates = < (double , double )> [];
295323 for (int week = 0 ; week < values.length; week++ ) {
296- if (displayMode == DisplayMode .stacked) {
297- lastestDownloads[week] += values[week][versionRange];
324+ final value = displayMode == DisplayMode .percentage
325+ ? values[week][versionRange] * 100 / totals[week]
326+ : values[week][versionRange];
327+
328+ if (displayMode == DisplayMode .unstacked) {
329+ latestDownloads[week] = value;
298330 } else {
299- lastestDownloads [week] = values[week][versionRange] ;
331+ latestDownloads [week] += value ;
300332 }
333+
301334 final (x, y) = computeCoordinates (
302335 computeDateForWeekNumber (newestDate, values.length, week),
303- lastestDownloads [week]);
336+ latestDownloads [week]);
304337 lineCoordinates.add ((x, y));
305338 }
306339 lines.add (lineCoordinates);
@@ -349,7 +382,8 @@ void drawChart(
349382 path.setAttribute ('clip-path' , 'url(#clipRect)' );
350383 chart.append (path);
351384
352- if (displayMode == DisplayMode .stacked) {
385+ if (displayMode == DisplayMode .stacked ||
386+ displayMode == DisplayMode .percentage) {
353387 final prevLine = i == lines.length - 1
354388 ? [(xZero, yZero), (xMax, yZero)]
355389 : lines[lines.length - 1 - i - 1 ];
@@ -450,20 +484,26 @@ void drawChart(
450484
451485 final downloads = values[nearestIndex];
452486 for (int i = 0 ; i < downloads.length; i++ ) {
453- final index = ranges.length - 1 - i;
454- if (downloads[index ] > 0 ) {
487+ final rangeIndex = ranges.length - 1 - i;
488+ if (downloads[rangeIndex ] > 0 ) {
455489 // We only show the exact download count in the tooltip if it is non-zero.
456490 final square = HTMLDivElement ()
457491 ..setAttribute (
458492 'class' , 'downloads-chart-tooltip-square ${squareColorClass (i )}' );
459- final rangeText = HTMLSpanElement ()..text = '${ranges [index ]}: ' ;
493+ final rangeText = HTMLSpanElement ()..text = '${ranges [rangeIndex ]}: ' ;
460494 final tooltipRange = HTMLDivElement ()
461495 ..setAttribute ('class' , 'downloads-chart-tooltip-row' )
462496 ..append (square)
463497 ..append (rangeText);
498+
499+ final suffix = (displayMode == DisplayMode .percentage)
500+ ? '(${(downloads [rangeIndex ] * 100 / totals [nearestIndex ]).toStringAsPrecision (2 )}%)'
501+ : '' ;
502+ final text =
503+ '${formatWithThousandSeperators (downloads [rangeIndex ])}$suffix ' ;
464504 final downloadsText = HTMLSpanElement ()
465505 ..setAttribute ('class' , 'downloads-chart-tooltip-downloads' )
466- ..text = '${ formatWithThousandSeperators ( downloads [ index ])}' ;
506+ ..text = text ;
467507 final tooltipRow = HTMLDivElement ()
468508 ..setAttribute ('class' , 'downloads-chart-tooltip-row' )
469509 ..append (tooltipRange)
0 commit comments