Commit 631d4972 authored by Almouhannad Hafez's avatar Almouhannad Hafez

Add tooltip to line chart

parent f8041687
......@@ -10,7 +10,7 @@
<body>
<div id="scatterplot-container" style="display: none;">
<div id="scatterplot-tooltip">fdsfds</div>
<div id="scatterplot-tooltip"></div>
<svg id="scatterplot"></svg>
<ul class="legend" id="scatterplot-legend">
<p>Filter by level:</p>
......@@ -20,13 +20,14 @@
</ul>
</div>
<div id="area-chart-container">
<div id="line-chart-container">
<div id="line-chart-tooltip"></div>
<div class="heading">
<p class="title">S&P 500 Index</p>
<span class="select-text">Start year</span><input type="number" min="2015" max="2020" value="2015"
onkeydown="return false;" id="area-chart-year-input">
onkeydown="return false;" id="line-chart-year-input">
</div>
<svg id="area-chart"></svg>
<svg id="line-chart"></svg>
</div>
<script type="module" src="/src/ts/main.ts"></script>
</body>
......
......@@ -117,28 +117,41 @@ body {
/* #region Area chart */
#area-chart-container .area {
#line-chart-container .area {
fill: #e9eff5;
position: relative;
}
#area-chart-container .line {
#line-chart-container .line {
fill: none;
stroke: #537591;
stroke-width: 2.5px;
}
#area-chart-container .title {
#line-chart-container .title {
font-weight: 900;
font-size: 40px;
}
#area-chart-container .select-text {
#line-chart-container .select-text {
font-weight: 900;
margin-right: 0.5em;
}
#area-chart-container .heading {
#line-chart-container .heading {
margin-left: 3em;
}
#line-chart-tooltip {
position: absolute;
display: none;
text-align: center;
padding: 5px;
background: #537591;
color: white;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/* #endregion */
\ No newline at end of file
......@@ -3,20 +3,23 @@ import { Chart } from "./chart";
import { ChartConfiguration } from "./chartConfiguration";
export class AreaChart extends Chart {
xScale: any;
yScale: any;
xAxis: any;
yAxis: any;
xAxisGroup: any;
yAxisGroup: any;
lineGenerator: d3.Line<[number, number]>;
areaGenerator: d3.Area<[number, number]>;
xValue: (d: any) => any;
yValue: (d: any) => any;
private xScale: any;
private yScale: any;
private xAxis: any;
private yAxis: any;
private xAxisGroup: any;
private yAxisGroup: any;
private lineGenerator: d3.Line<[number, number]>;
private xValue: (d: any) => any;
private yValue: (d: any) => any;
private tooltip: d3.Selection<d3.BaseType, unknown, HTMLElement, any>;
private bisectDate: (array: ArrayLike<any>, x: any, lo?: number, hi?: number) => number;
private tooltipCircle: any;
constructor(_config: ChartConfiguration, _data?: any[]) {
super(_config, _data);
this.bisectDate = d3.bisector((d: any) => d.date).left;
this.initVis();
}
protected getDefaultMargins(): { top: number; right: number; bottom: number; left: number; } {
......@@ -67,42 +70,16 @@ export class AreaChart extends Chart {
.x((d: any) => vis.xScale(d.date))
.y((d: any) => vis.yScale(d.close));
// Define area generator
vis.areaGenerator = d3.area()
.x((d: any) => vis.xScale(d.date))
.y0(vis.height)
.y1((d: any) => vis.yScale(d.close));
// tooltip
vis.tooltip = d3.select("#line-chart-tooltip");
vis.tooltipCircle = vis.chart.append("circle")
.attr("r", 5)
.attr("stroke", "green")
.attr("fill", "white")
.style("display", "none");
}
protected renderVis(): void {
const vis = this;
// Bind data to area paths
const areaSelection = vis.chart.selectAll(".area")
.data([vis.data]); // Wrap data in an array for a single area
// Enter selection for area
areaSelection.enter()
.append("path")
.attr("class", "area")
.attr("d", vis.areaGenerator)
.transition()
.duration(1000)
.attr("opacity", 1);
// Update selection for area
areaSelection
.transition()
.duration(1000)
.attr("d", vis.areaGenerator);
// Exit selection for area
areaSelection.exit()
.transition()
.duration(1000)
.attr("opacity", 0)
.remove();
// Bind data to line paths
const lineSelection = vis.chart.selectAll(".line")
.data([vis.data]);
......@@ -140,6 +117,41 @@ export class AreaChart extends Chart {
.duration(1000)
.call(vis.yAxis);
// Create a transparent tracking area
vis.chart.append('rect')
.attr('width', vis.width)
.attr('height', vis.height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseenter', () => {
vis.tooltip.style('display', 'block');
vis.tooltipCircle.style('display', 'block');
})
.on('mouseleave', () => {
vis.tooltip.style('display', 'none');
vis.tooltipCircle.style('display', 'none');
})
.on('mousemove', (event: any) => {
const xPos = d3.pointer(event, this)[0] - vis.config.margin.left;
const date = vis.xScale.invert(xPos);
// Use the bisector to find the nearest left point
const index = vis.bisectDate(vis.data, date, 1);
const a = vis.data[index - 1];
const b = vis.data[index];
const d = b && (date - a.date > b.date - date) ? b : a; // seelect nearest point
if (d) {
vis.tooltip.transition()
vis.tooltip.html(`Close: ${d.close}`)
.style("left", (event.pageX + 5) + "px")
.style("top", (event.pageY - 28) + "px");
vis.tooltipCircle
.attr("cx", vis.xScale(d.date))
.attr("cy", vis.yScale(d.close));
}
});
}
public updateVis(): void {
const vis = this;
......
import { AreaChart } from './areaChart';
import { ChartConfiguration } from './chartConfiguration';
import { AreaChart } from './lineChart';
import { Scatterplot } from './scatter';
import '/src/css/style.css';
import * as d3 from 'd3';
......@@ -53,9 +54,9 @@ import * as d3 from 'd3';
* Load data from CSV file asynchronously and render scatter plot
*/
const areaChart = new AreaChart(new ChartConfiguration("#area-chart"));
const areaChart = new AreaChart(new ChartConfiguration("#line-chart"));
let areaChartData: any[];
d3.select('#area-chart-year-input')
d3.select('#line-chart-year-input')
.on('input', function () {
// Get the current value of the input
let value = d3.select(this).property('value');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment