From e8e56dbc078a2cfcdfe0455ec29345b2ed3974e3 Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Fri, 28 Feb 2025 13:00:36 +0000 Subject: [PATCH] Downloads chart : highlight areas on hover in stacked and percentage display mode --- .../src/widget/downloads_chart/widget.dart | 75 ++++++++++++------- pkg/web_css/lib/src/_pkg.scss | 4 + 2 files changed, 52 insertions(+), 27 deletions(-) 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 a0ff3f8a20..a8b563b690 100644 --- a/pkg/web_app/lib/src/widget/downloads_chart/widget.dart +++ b/pkg/web_app/lib/src/widget/downloads_chart/widget.dart @@ -198,6 +198,7 @@ void drawChart( {DisplayMode displayMode = DisplayMode.unstacked}) { final ranges = displayLists.ranges; final values = displayLists.weekLists; + final displayAreas = displayMode != DisplayMode.unstacked; if (values.isEmpty) return; @@ -375,30 +376,25 @@ void drawChart( lines.add(lineCoordinates); } - StringBuffer computeLinePath(List<(double, double)> coordinates) { - final path = StringBuffer(); - var command = 'M'; - coordinates.forEach((c) { - path.write(' $command${c.$1} ${c.$2}'); - command = 'L'; - }); - return path; + final areas = >[]; + if (displayAreas) { + for (int i = 0; i < ranges.length; i++) { + final prevLine = i == 0 ? [(xZero, yZero), (xMax, yZero)] : lines[i - 1]; + final areaLine = [...lines[i], ...prevLine.reversed, lines[i].first]; + areas.add(areaLine); + } } - StringBuffer computeAreaPath(List<(double, double)> topCoordinates, - List<(double, double)> bottomCoordinates) { + final linePaths = []; + final areaPaths = []; + + StringBuffer computeLinePath(List<(double, double)> coordinates) { final path = StringBuffer(); var command = 'M'; - topCoordinates.forEach((c) { - path.write(' $command${c.$1} ${c.$2}'); - command = 'L'; - }); - - bottomCoordinates.reversed.forEach((c) { + coordinates.forEach((c) { path.write(' $command${c.$1} ${c.$2}'); command = 'L'; }); - path.write('Z'); return path; } @@ -408,11 +404,10 @@ void drawChart( final legendWidth = 20; final legendHeight = 8; - final linePaths = []; for (int i = 0; i < lines.length; i++) { - // We add the lines in reverse order so that the newest versions get the - // main colors. - final line = computeLinePath(lines[lines.length - 1 - i]); + // We add the lines and areas in reverse order so that the newest versions + // get the main colors. + final line = computeLinePath(lines[ranges.length - 1 - i]); final path = SVGPathElement(); path.setAttribute('class', '${strokeColorClass(i)} downloads-chart-line '); path.setAttribute('d', '$line'); @@ -420,16 +415,13 @@ void drawChart( linePaths.add(path); chart.append(path); - if (displayMode == DisplayMode.stacked || - displayMode == DisplayMode.percentage) { - final prevLine = i == lines.length - 1 - ? [(xZero, yZero), (xMax, yZero)] - : lines[lines.length - 1 - i - 1]; - final areaPath = computeAreaPath(lines[lines.length - 1 - i], prevLine); + if (displayAreas) { + final areaPath = computeLinePath(areas[ranges.length - 1 - i]); final area = SVGPathElement(); area.setAttribute('class', '${fillColorClass(i)} downloads-chart-area '); area.setAttribute('d', '$areaPath'); area.setAttribute('clip-path', 'url(#clipRect)'); + areaPaths.add(area); chart.append(area); } @@ -487,6 +479,12 @@ void drawChart( final l = linePaths[i]; l.removeAttribute('class'); l.setAttribute('class', '${strokeColorClass(i)} downloads-chart-line'); + + if (displayAreas) { + areaPaths[i].removeAttribute('class'); + areaPaths[i] + .setAttribute('class', '${fillColorClass(i)} downloads-chart-area'); + } } } @@ -527,6 +525,14 @@ void drawChart( highlightRangeIndices.add(i); } } + if (displayAreas) { + for (int i = 0; i < areas.length; i++) { + final a = areas[i]; + if (isPointInPolygon(a, (xPosition, yPosition))) { + highlightRangeIndices.add(i); + } + } + } final coords = computeCoordinates(selectedDay, 0); cursor.setAttribute('transform', 'translate(${coords.$1}, 0)'); @@ -557,6 +563,9 @@ void drawChart( ..text = text; linePaths[i].removeAttribute('class'); + if (displayAreas) { + areaPaths[i].removeAttribute('class'); + } if (highlightRangeIndices.contains(rangeIndex)) { rangeText.setAttribute('class', 'downloads-chart-tooltip-highlight'); @@ -564,12 +573,24 @@ void drawChart( 'class', 'downloads-chart-tooltip-highlight'); linePaths[i].setAttribute( 'class', '${strokeColorClass(i)} downloads-chart-line'); + if (displayAreas) { + areaPaths[i].setAttribute( + 'class', '${fillColorClass(i)} downloads-chart-area'); + } } else if (highlightRangeIndices.isNotEmpty) { linePaths[i].setAttribute( 'class', '${strokeColorClass(i)} downloads-chart-line-faded'); + if (displayAreas) { + areaPaths[i].setAttribute( + 'class', '${fillColorClass(i)} downloads-chart-area-faded'); + } } else { linePaths[i].setAttribute( 'class', '${strokeColorClass(i)} downloads-chart-line'); + if (displayAreas) { + areaPaths[i].setAttribute( + 'class', '${fillColorClass(i)} downloads-chart-area '); + } } final tooltipRange = HTMLDivElement() ..setAttribute('class', 'downloads-chart-tooltip-row') diff --git a/pkg/web_css/lib/src/_pkg.scss b/pkg/web_css/lib/src/_pkg.scss index 82fb9464cb..686f4dc79c 100644 --- a/pkg/web_css/lib/src/_pkg.scss +++ b/pkg/web_css/lib/src/_pkg.scss @@ -466,6 +466,10 @@ opacity: 0.3; } + .downloads-chart-area-faded { + opacity: 0.1; + } + .downloads-chart-stroke-blue { stroke: var(--pub-downloads-chart-color-0); }