@@ -26,6 +26,11 @@ String strokeColorClass(int i) => 'downloads-chart-stroke-${colors[i]}';
2626String fillColorClass (int i) => 'downloads-chart-fill-${colors [i ]}' ;
2727String squareColorClass (int i) => 'downloads-chart-square-${colors [i ]}' ;
2828
29+ enum DisplayMode {
30+ stacked,
31+ unstacked,
32+ }
33+
2934void create (HTMLElement element, Map <String , String > options) {
3035 final dataPoints = options['points' ];
3136 if (dataPoints == null ) {
@@ -36,6 +41,12 @@ void create(HTMLElement element, Map<String, String> options) {
3641 if (versionsRadio == null ) {
3742 throw UnsupportedError ('data-downloads-chart-versions-radio required' );
3843 }
44+
45+ final displayRadio = options['display-radio' ];
46+ if (displayRadio == null ) {
47+ throw UnsupportedError ('data-downloads-chart-display-radio required' );
48+ }
49+
3950 Element createNewSvg () {
4051 return document.createElementNS ('http://www.w3.org/2000/svg' , 'svg' )
4152 ..setAttribute ('height' , '100%' )
@@ -72,6 +83,9 @@ void create(HTMLElement element, Map<String, String> options) {
7283 weeksToDisplay,
7384 );
7485
86+ var currentDisplayList = majorDisplayLists;
87+ var currentDisplayMode = DisplayMode .unstacked;
88+
7589 final versionModesLists = {
7690 'major' : majorDisplayLists,
7791 'minor' : minorDisplayLists,
@@ -91,7 +105,34 @@ void create(HTMLElement element, Map<String, String> options) {
91105 element.removeChild (svg);
92106 svg = createNewSvg ();
93107 element.append (svg);
94- drawChart (svg, toolTip, displayList, data.newestDate);
108+ currentDisplayList = displayList;
109+ drawChart (svg, toolTip, displayList, data.newestDate,
110+ displayMode: currentDisplayMode);
111+ });
112+ });
113+
114+ final displayModesMap = < String , DisplayMode > {
115+ 'stacked' : DisplayMode .stacked,
116+ 'unstacked' : DisplayMode .unstacked
117+ };
118+
119+ final displayModes = document.getElementsByName (displayRadio).toList ();
120+ displayModes.forEach ((i) {
121+ final radioButton = i as HTMLInputElement ;
122+ final value = radioButton.value;
123+ final displayMode = displayModesMap[value];
124+
125+ if (displayMode == null ) {
126+ throw UnsupportedError ('Unsupported display-radio value: "$value "' );
127+ }
128+
129+ radioButton.onClick.listen ((e) {
130+ element.removeChild (svg);
131+ svg = createNewSvg ();
132+ element.append (svg);
133+ currentDisplayMode = displayMode;
134+ drawChart (svg, toolTip, currentDisplayList, data.newestDate,
135+ displayMode: displayMode);
95136 });
96137 });
97138
@@ -103,7 +144,7 @@ void drawChart(
103144 HTMLDivElement toolTip,
104145 ({List <String > ranges, List <List <int >> weekLists}) displayLists,
105146 DateTime newestDate,
106- {bool stacked = false }) {
147+ {DisplayMode displayMode = DisplayMode .unstacked }) {
107148 final ranges = displayLists.ranges;
108149 final values = displayLists.weekLists;
109150
@@ -128,8 +169,11 @@ void drawChart(
128169 /// Computes max value on y-axis such that we get a nice division for the
129170 /// interval length between the numbers shown by the ticks on the y axis.
130171 (int maxY, int interval) computeMaxYAndInterval (List <List <int >> values) {
131- final maxDownloads =
132- values.fold <int >(1 , (a, b) => math.max <int >(a, b.reduce (math.max)));
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)));
176+
133177 final digits = maxDownloads.toString ().length;
134178 final buffer = StringBuffer ()..write ('1' );
135179 if (digits > 2 ) {
@@ -244,14 +288,20 @@ void drawChart(
244288
245289 // Chart lines and legends
246290
291+ final lastestDownloads = List .filled (values.length, 0 );
247292 final lines = < StringBuffer > [];
248293 for (int versionRange = 0 ; versionRange < values[0 ].length; versionRange++ ) {
249294 final line = StringBuffer ();
250295 var c = 'M' ;
251296 for (int week = 0 ; week < values.length; week++ ) {
297+ if (displayMode == DisplayMode .stacked) {
298+ lastestDownloads[week] += values[week][versionRange];
299+ } else {
300+ lastestDownloads[week] = values[week][versionRange];
301+ }
252302 final (x, y) = computeCoordinates (
253303 computeDateForWeekNumber (newestDate, values.length, week),
254- values [week][versionRange ]);
304+ lastestDownloads [week]);
255305 line.write (' $c $x $y ' );
256306 c = 'L' ;
257307 }
@@ -265,14 +315,32 @@ void drawChart(
265315 final legendHeight = 8 ;
266316
267317 for (int i = 0 ; i < lines.length; i++ ) {
268- final path = SVGPathElement ();
269- path.setAttribute ('class' , '${strokeColorClass (i )} downloads-chart-line ' );
270318 // We assign colors in reverse order so that main colors are chosen first for
271319 // the newest versions.
272- path.setAttribute ('d' , '${lines [lines .length - 1 - i ]}' );
320+ final line = lines[lines.length - 1 - i];
321+
322+ final path = SVGPathElement ();
323+ path.setAttribute ('class' , '${strokeColorClass (i )} downloads-chart-line ' );
324+ path.setAttribute ('d' , '$line ' );
273325 path.setAttribute ('clip-path' , 'url(#clipRect)' );
274326 chart.append (path);
275327
328+ if (displayMode == DisplayMode .stacked) {
329+ final area = SVGPathElement ();
330+ area.setAttribute ('class' , '${fillColorClass (i )} downloads-chart-area ' );
331+ final prevLine = i == lines.length - 1
332+ ? ' M $xZero $yZero L$xMax $yZero '
333+ : lines[lines.length - 1 - i - 1 ];
334+ final reversed = prevLine
335+ .toString ()
336+ .replaceAll (' M' , '' )
337+ .split ('L' )
338+ .reversed
339+ .join ('L' );
340+ area.setAttribute ('d' , '$line L$reversed Z' );
341+ chart.append (area);
342+ }
343+
276344 final legend = SVGRectElement ();
277345 chart.append (legend);
278346 legend.setAttribute ('class' ,
0 commit comments