diff --git a/app/lib/frontend/dom/material.dart b/app/lib/frontend/dom/material.dart index d0958e050b..c7c1464435 100644 --- a/app/lib/frontend/dom/material.dart +++ b/app/lib/frontend/dom/material.dart @@ -436,3 +436,31 @@ d.Node option({ ], ); } + +d.Node radioButtons({ + required String name, + required List<({String label, String value, String id})> radios, + String? initialValue, + Iterable? classes, + String? leadingText, +}) { + final nodes = []; + if (leadingText != null) { + nodes.add(d.strong(text: leadingText)); + } + radios.forEach((e) { + nodes.add(d.input( + id: e.id, + type: 'radio', + name: name, + value: e.value, + classes: [ + ...?classes, + ], + attributes: {if (e.value == initialValue) 'checked': ''}, + )); + nodes.add(d.label(attributes: {'for': e.id}, child: d.text(e.label))); + }); + + return d.div(classes: ['mdc-form-field'], children: nodes); +} diff --git a/app/lib/frontend/templates/views/pkg/score_tab.dart b/app/lib/frontend/templates/views/pkg/score_tab.dart index 90d32d941f..c061924308 100644 --- a/app/lib/frontend/templates/views/pkg/score_tab.dart +++ b/app/lib/frontend/templates/views/pkg/score_tab.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'package:_pub_shared/data/download_counts_data.dart'; import 'package:_pub_shared/format/number_format.dart'; import 'package:pana/models.dart'; +import 'package:pub_dev/frontend/dom/material.dart'; import 'package:pub_dev/service/download_counts/backend.dart'; import 'package:pub_dev/shared/utils.dart'; @@ -179,18 +180,35 @@ d.Node _section(ReportSection section) { } d.Node _downloadsChart(WeeklyVersionDownloadCounts weeklyVersionDownloads) { + final versionModes = d.div( + classes: ['downloads-chart-version-modes'], + children: [ + radioButtons( + leadingText: 'By versions: ', + name: 'version-modes', + radios: [ + (id: 'version-modes-major', value: 'major', label: 'Major'), + (id: 'version-modes-minor', value: 'minor', label: 'Minor'), + (id: 'version-modes-patch', value: 'patch', label: 'Patch') + ], + classes: ['downloads-chart-radio-button'], + initialValue: 'major') + ], + ); final container = d.div( classes: ['downloads-chart'], id: '-downloads-chart', attributes: { 'data-widget': 'downloads-chart', 'data-downloads-chart-points': - base64Encode(jsonUtf8Encoder.convert(weeklyVersionDownloads)) + base64Encode(jsonUtf8Encoder.convert(weeklyVersionDownloads)), + 'data-downloads-chart-versions-radio': 'version-modes', }, ); return d.fragment([ d.h1(text: 'Weekly Downloads over the last 40 weeks'), + versionModes, container, ]); } diff --git a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart index ea7223bf29..7d1191aa72 100644 --- a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart +++ b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart @@ -9,6 +9,7 @@ import 'package:_pub_shared/data/download_counts_data.dart'; import 'package:_pub_shared/format/date_format.dart'; import 'package:_pub_shared/format/number_format.dart'; import 'package:web/web.dart'; +import 'package:web_app/src/web_util.dart'; import 'computations.dart'; @@ -30,15 +31,19 @@ void create(HTMLElement element, Map options) { throw UnsupportedError('data-downloads-chart-points required'); } + final versionsRadio = options['versions-radio']; + if (versionsRadio == null) { + throw UnsupportedError('data-downloads-chart-versions-radio required'); + } + final svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('height', '100%'); svg.setAttribute('width', '100%'); - element.append(svg); + final data = WeeklyVersionDownloadCounts.fromJson((utf8.decoder .fuse(json.decoder) .convert(base64Decode(dataPoints)) as Map)); - final weeksToDisplay = math.min(40, data.totalWeeklyDownloads.length); final majorDisplayLists = prepareWeekLists( @@ -47,6 +52,38 @@ void create(HTMLElement element, Map options) { weeksToDisplay, ); + final minorDisplayLists = prepareWeekLists( + data.totalWeeklyDownloads, + data.minorRangeWeeklyDownloads, + weeksToDisplay, + ); + + final patchDisplayLists = prepareWeekLists( + data.totalWeeklyDownloads, + data.patchRangeWeeklyDownloads, + weeksToDisplay, + ); + + final versionModesLists = { + 'major': majorDisplayLists, + 'minor': minorDisplayLists, + 'patch': patchDisplayLists + }; + + final versionModes = document.getElementsByName(versionsRadio).toList(); + versionModes.forEach((i) { + final radioButton = i as HTMLInputElement; + final value = radioButton.value; + final displayList = versionModesLists[value]; + + if (displayList == null) { + throw UnsupportedError('Unsupported versions-radio value: "$value"'); + } + radioButton.onClick.listen((e) { + drawChart(svg, displayList, data.newestDate); + }); + }); + drawChart(svg, majorDisplayLists, data.newestDate); } @@ -111,7 +148,7 @@ void drawChart( } final chart = SVGGElement(); - svg.append(chart); + svg.replaceChildren(chart); // Axis and ticks diff --git a/pkg/web_css/lib/src/_pkg.scss b/pkg/web_css/lib/src/_pkg.scss index 3e43828e07..a92798a28f 100644 --- a/pkg/web_css/lib/src/_pkg.scss +++ b/pkg/web_css/lib/src/_pkg.scss @@ -289,6 +289,14 @@ padding-top: 16px; } + .downloads-chart-version-modes { + float: right; + } + + .downloads-chart-radio-button { + margin-left: 10px; + } + .downloads-chart-frame { fill:none; stroke-width: 1;