From 69b5221e24e44feace67fde20747d38b236f183b Mon Sep 17 00:00:00 2001
From: Sarah Zakarias
Date: Wed, 19 Feb 2025 12:09:34 +0000
Subject: [PATCH] Downloads chart: resize chart on screen size changes
---
app/lib/frontend/dom/material.dart | 11 ++--
app/test/frontend/golden/pkg_score_page.html | 40 +++++++++-----
.../pkg_score_page_with_downloads_chart.html | 40 +++++++++-----
.../src/widget/downloads_chart/widget.dart | 54 +++++++++++++++----
pkg/web_css/lib/src/_pkg.scss | 21 +++++++-
pkg/web_css/lib/src/_variables.scss | 4 ++
6 files changed, 126 insertions(+), 44 deletions(-)
diff --git a/app/lib/frontend/dom/material.dart b/app/lib/frontend/dom/material.dart
index c7c1464435..8262b85cfe 100644
--- a/app/lib/frontend/dom/material.dart
+++ b/app/lib/frontend/dom/material.dart
@@ -449,18 +449,17 @@ d.Node radioButtons({
nodes.add(d.strong(text: leadingText));
}
radios.forEach((e) {
- nodes.add(d.input(
+ final button = (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)));
+ final label = d.label(attributes: {'for': e.id}, child: d.text(e.label));
+ final div = d.div(children: [button, label]);
+ nodes.add(div);
});
- return d.div(classes: ['mdc-form-field'], children: nodes);
+ return d.div(classes: ['mdc-form-field', ...?classes], children: nodes);
}
diff --git a/app/test/frontend/golden/pkg_score_page.html b/app/test/frontend/golden/pkg_score_page.html
index ec19dbb5ae..3f28ec74de 100644
--- a/app/test/frontend/golden/pkg_score_page.html
+++ b/app/test/frontend/golden/pkg_score_page.html
@@ -308,25 +308,37 @@
Weekly downloads
-
-
diff --git a/app/test/frontend/golden/pkg_score_page_with_downloads_chart.html b/app/test/frontend/golden/pkg_score_page_with_downloads_chart.html
index ec19dbb5ae..3f28ec74de 100644
--- a/app/test/frontend/golden/pkg_score_page_with_downloads_chart.html
+++ b/app/test/frontend/golden/pkg_score_page_with_downloads_chart.html
@@ -308,25 +308,37 @@
Weekly downloads
-
-
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 77cbf2b0bc..acea2a6bac 100644
--- a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart
+++ b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
+import 'dart:js_interop';
import 'dart:math' as math;
import 'package:_pub_shared/data/download_counts_data.dart';
@@ -111,6 +112,7 @@ void create(HTMLElement element, Map
options) {
currentDisplayList = displayList;
drawChart(
svg,
+ svg.getBoundingClientRect().width,
toolTip,
displayList,
data.newestDate,
@@ -143,6 +145,7 @@ void create(HTMLElement element, Map options) {
currentDisplayMode = displayMode;
drawChart(
svg,
+ svg.getBoundingClientRect().width,
toolTip,
currentDisplayList,
data.newestDate,
@@ -152,17 +155,41 @@ void create(HTMLElement element, Map options) {
});
});
+ void resize(double width) {
+ element.removeChild(svg);
+ svg = createNewSvg();
+ element.append(svg);
+
+ drawChart(
+ svg,
+ width,
+ toolTip,
+ currentDisplayList,
+ data.newestDate,
+ totals,
+ );
+ }
+
+ final resizeObserver = ResizeObserver(
+ (JSArray a, ResizeObserverBoxOptions b) {
+ resize(a.toDart[0].contentRect.width);
+ }.toJS);
+
drawChart(
svg,
+ svg.getBoundingClientRect().width,
toolTip,
- majorDisplayLists,
+ currentDisplayList,
data.newestDate,
totals,
);
+
+ resizeObserver.observe(element);
}
void drawChart(
Element svg,
+ double width,
HTMLDivElement toolTip,
({List ranges, List> weekLists}) displayLists,
DateTime newestDate,
@@ -173,8 +200,7 @@ void drawChart(
if (values.isEmpty) return;
- final frameWidth =
- 775; // TODO(zarah): Investigate if this width can be dynamic
+ final frameWidth = width;
final topPadding = 30;
final leftPadding = 30;
final rightPadding = 70; // Make extra room for labels on y-axis
@@ -266,8 +292,15 @@ void drawChart(
final tickLabel = SVGTextElement();
chart.append(tickLabel);
- tickLabel.setAttribute(
- 'class', 'downloads-chart-tick-label downloads-chart-tick-label-x');
+
+ if (week % 8 == 0) {
+ // We skip every other label on small screens.
+ tickLabel.setAttribute('class',
+ 'downloads-chart-tick-label downloads-chart-anchored-tick-label-x');
+ } else {
+ tickLabel.setAttribute(
+ 'class', 'downloads-chart-tick-label downloads-chart-tick-label-x');
+ }
tickLabel.text = formatAbbrMonthDay(date);
tickLabel.setAttribute('y', '$tickLabelYCoordinate');
tickLabel.setAttribute('x', '$x');
@@ -460,12 +493,15 @@ void drawChart(
}
cursor.setAttribute('style', 'opacity:1');
- toolTip.setAttribute(
- 'style',
- 'top:${e.y + toolTipOffsetFromMouse + document.scrollingElement!.scrollTop}px;'
- 'left:${e.x}px;');
final pointPercentage = (e.x - boundingRect.x - xZero) / chartWidth;
+ final horizontalPosition =
+ e.x + toolTip.getBoundingClientRect().width > width
+ ? 'left:${e.x - toolTip.getBoundingClientRect().width}px;'
+ : 'left:${e.x}px;';
+ toolTip.setAttribute('style',
+ 'top:${e.y + toolTipOffsetFromMouse + document.scrollingElement!.scrollTop}px;$horizontalPosition');
+
final nearestIndex = ((values.length - 1) * pointPercentage).round();
final selectedDay =
computeDateForWeekNumber(newestDate, values.length, nearestIndex);
diff --git a/pkg/web_css/lib/src/_pkg.scss b/pkg/web_css/lib/src/_pkg.scss
index 3fd6bbf33d..2a48bc531b 100644
--- a/pkg/web_css/lib/src/_pkg.scss
+++ b/pkg/web_css/lib/src/_pkg.scss
@@ -279,13 +279,17 @@
.downloads-chart {
display: flex;
height: 700px;
- width: 775px;
+ width: 100%;
flex-direction: column;
margin-top: 16px;
}
.downloads-chart-version-modes {
float: right;
+
+ @media (max-width: variables.$downloads-chart-display-max-width) {
+ float: left;
+ }
}
.downloads-chart-display-modes {
@@ -293,7 +297,14 @@
}
.downloads-chart-radio-button {
+ display: flex;
+ flex-direction: row;
margin-left: 10px;
+
+ @media (max-width: variables.$downloads-chart-radio-max-width) {
+ display: flex;
+ flex-direction: column;
+ }
}
.downloads-chart-axis-line {
@@ -358,6 +369,14 @@
.downloads-chart-tick-label-x {
text-anchor: middle;
+ @media (max-width: variables.$downloads-chart-label-max-width) {
+ display: none;
+ }
+ }
+
+ // These labels are not removed even if the screen gets smaller.
+ .downloads-chart-anchored-tick-label-x {
+ text-anchor: middle;
}
.downloads-chart-tick-label-y {
diff --git a/pkg/web_css/lib/src/_variables.scss b/pkg/web_css/lib/src/_variables.scss
index 1faf5d0227..275a1d19a3 100644
--- a/pkg/web_css/lib/src/_variables.scss
+++ b/pkg/web_css/lib/src/_variables.scss
@@ -301,6 +301,10 @@ $device-desktop-min-width: 641px;
$device-mobile-max-width: 640px;
$device-tablet-max-width: 979px;
+$downloads-chart-label-max-width: 825px;
+$downloads-chart-display-max-width: 1025px;
+$downloads-chart-radio-max-width: 650px;
+
$z-index-nav-mask: 1000;
$site-max-width: 1136px;