Skip to content

Commit 0878f93

Browse files
szakariasisoos
authored andcommitted
Downloads chart: highlight line chart and tooltip row on hover (dart-lang#8601)
1 parent f44e403 commit 0878f93

File tree

2 files changed

+61
-10
lines changed

2 files changed

+61
-10
lines changed

pkg/web_app/lib/src/widget/downloads_chart/widget.dart

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ void drawChart(
408408
final legendWidth = 20;
409409
final legendHeight = 8;
410410

411+
final linePaths = <SVGPathElement>[];
411412
for (int i = 0; i < lines.length; i++) {
412413
// We add the lines in reverse order so that the newest versions get the
413414
// main colors.
@@ -416,6 +417,7 @@ void drawChart(
416417
path.setAttribute('class', '${strokeColorClass(i)} downloads-chart-line ');
417418
path.setAttribute('d', '$line');
418419
path.setAttribute('clip-path', 'url(#clipRect)');
420+
linePaths.add(path);
419421
chart.append(path);
420422

421423
if (displayMode == DisplayMode.stacked ||
@@ -475,11 +477,17 @@ void drawChart(
475477

476478
// Setup mouse handling
477479

478-
DateTime? lastSelectedDay;
479480
void hideCursor(_) {
480481
cursor.setAttribute('style', 'opacity:0');
481482
toolTip.setAttribute('style', 'opacity:0;position:absolute;');
482-
lastSelectedDay = null;
483+
}
484+
485+
void resetHighlights() {
486+
for (int i = 0; i < linePaths.length; i++) {
487+
final l = linePaths[i];
488+
l.removeAttribute('class');
489+
l.setAttribute('class', '${strokeColorClass(i)} downloads-chart-line');
490+
}
483491
}
484492

485493
hideCursor(1);
@@ -491,13 +499,16 @@ void drawChart(
491499
e.y < boundingRect.y + yMax ||
492500
e.y > boundingRect.y + yZero) {
493501
// We are outside the actual chart area
502+
resetHighlights();
494503
hideCursor(1);
495504
return;
496505
}
497506

498507
cursor.setAttribute('style', 'opacity:1');
508+
final yPosition = e.y - boundingRect.y;
509+
final xPosition = e.x - boundingRect.x;
510+
final pointPercentage = (xPosition - xZero) / chartWidth;
499511

500-
final pointPercentage = (e.x - boundingRect.x - xZero) / chartWidth;
501512
final horizontalPosition =
502513
e.x + toolTip.getBoundingClientRect().width > width
503514
? 'left:${e.x - toolTip.getBoundingClientRect().width}px;'
@@ -508,7 +519,14 @@ void drawChart(
508519
final nearestIndex = ((values.length - 1) * pointPercentage).round();
509520
final selectedDay =
510521
computeDateForWeekNumber(newestDate, values.length, nearestIndex);
511-
if (selectedDay == lastSelectedDay) return;
522+
523+
// Determine if we are close enough to highlight one or more version ranges
524+
final highlightRangeIndices = <int>{};
525+
for (int i = 0; i < lines.length; i++) {
526+
if (isPointOnPathWithTolerance(lines[i], (xPosition, yPosition), 5)) {
527+
highlightRangeIndices.add(i);
528+
}
529+
}
512530

513531
final coords = computeCoordinates(selectedDay, 0);
514532
cursor.setAttribute('transform', 'translate(${coords.$1}, 0)');
@@ -528,10 +546,6 @@ void drawChart(
528546
..setAttribute(
529547
'class', 'downloads-chart-tooltip-square ${squareColorClass(i)}');
530548
final rangeText = HTMLSpanElement()..text = '${ranges[rangeIndex]}: ';
531-
final tooltipRange = HTMLDivElement()
532-
..setAttribute('class', 'downloads-chart-tooltip-row')
533-
..append(square)
534-
..append(rangeText);
535549

536550
final suffix = (displayMode == DisplayMode.percentage)
537551
? ' (${(downloads[rangeIndex] * 100 / totals[nearestIndex]).toStringAsPrecision(2)}%)'
@@ -541,15 +555,38 @@ void drawChart(
541555
final downloadsText = HTMLSpanElement()
542556
..setAttribute('class', 'downloads-chart-tooltip-downloads')
543557
..text = text;
558+
559+
linePaths[i].removeAttribute('class');
560+
561+
if (highlightRangeIndices.contains(rangeIndex)) {
562+
rangeText.setAttribute('class', 'downloads-chart-tooltip-highlight');
563+
downloadsText.setAttribute(
564+
'class', 'downloads-chart-tooltip-highlight');
565+
linePaths[i].setAttribute(
566+
'class', '${strokeColorClass(i)} downloads-chart-line');
567+
} else if (highlightRangeIndices.isNotEmpty) {
568+
linePaths[i].setAttribute(
569+
'class', '${strokeColorClass(i)} downloads-chart-line-faded');
570+
} else {
571+
linePaths[i].setAttribute(
572+
'class', '${strokeColorClass(i)} downloads-chart-line');
573+
}
574+
final tooltipRange = HTMLDivElement()
575+
..setAttribute('class', 'downloads-chart-tooltip-row')
576+
..append(square)
577+
..append(rangeText);
578+
544579
final tooltipRow = HTMLDivElement()
545580
..setAttribute('class', 'downloads-chart-tooltip-row')
546581
..append(tooltipRange)
547582
..append(downloadsText);
548583
toolTip.append(tooltipRow);
549584
}
550585
}
551-
lastSelectedDay = selectedDay;
552586
});
553587

554-
svg.onMouseLeave.listen(hideCursor);
588+
svg.onMouseLeave.listen((e) {
589+
resetHighlights();
590+
hideCursor(1);
591+
});
555592
}

pkg/web_css/lib/src/_pkg.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,20 @@
342342
padding-left: 10px;
343343
}
344344

345+
.downloads-chart-tooltip-highlight {
346+
font-weight: bold;
347+
color:var(--pub-neutral-textColor);
348+
}
349+
350+
345351
.downloads-chart-tooltip-row {
346352
display: flex;
347353
flex-direction: row;
348354
align-items: center;
349355
justify-content: space-between;
350356
font-size: small;
351357
color: var(--pub-score_label-text-color);
358+
padding-right: 4px;
352359
}
353360

354361
.downloads-chart-cursor {
@@ -448,6 +455,13 @@
448455
stroke-linejoin: round;
449456
}
450457

458+
.downloads-chart-line-faded {
459+
fill: none;
460+
stroke-width: 2;
461+
stroke-linejoin: round;
462+
opacity: 0.3;
463+
}
464+
451465
.downloads-chart-area {
452466
opacity: 0.3;
453467
}

0 commit comments

Comments
 (0)