diff --git a/src/components/charts/DistributionPlot/DistributionPlot.re b/src/components/charts/DistributionPlot/DistributionPlot.re index ecc52b09..bd85a66c 100644 --- a/src/components/charts/DistributionPlot/DistributionPlot.re +++ b/src/components/charts/DistributionPlot/DistributionPlot.re @@ -86,6 +86,21 @@ module Styles = { selector(".lollipops-x-axis .domain", [display(`none)]), selector(".lollipops-x-axis .tick line", [display(`none)]), selector(".lollipops-x-axis .tick text", [display(`none)]), + selector( + ".lollipop-tooltip", + [ + position(`absolute), + textAlign(`center), + padding(px(2)), + backgroundColor(hex("bfcad4")), + borderRadius(px(3)), + ], + ), + selector( + ".lollipops-circle-mouseover", + [SVG.fill(hex("ffa500")), SVG.stroke(`hex("fff"))], + ), + selector(".lollipops-line-mouseover", [SVG.stroke(`hex("ffa500"))]), ]); }; @@ -126,4 +141,4 @@ let make = showVerticalLine /> ; -}; \ No newline at end of file +}; diff --git a/src/components/charts/DistributionPlot/distPlotD3.js b/src/components/charts/DistributionPlot/distPlotD3.js index 91250879..dfdc4af6 100644 --- a/src/components/charts/DistributionPlot/distPlotD3.js +++ b/src/components/charts/DistributionPlot/distPlotD3.js @@ -1,7 +1,11 @@ const _ = require('lodash'); const d3 = require('d3'); const moment = require('moment'); +require('./styles.css'); +/** + * @todo: To rename as "DistPlotD3". + */ export class CdfChartD3 { constructor() { @@ -93,11 +97,11 @@ export class CdfChartD3 { // Add svg. this.svg = this._container .createObject({ tag: 'svg', selector: 'svg-chart-container' }) - .attr('width', "100%") + .attr('width', '100%') .attr('height', this.attrs.svgHeight) .attr('pointer-events', 'none'); - // Add container "g" (empty) element. + // Add container 'g' (empty) element. this.chart = this.svg .createObject({ tag: 'g', selector: 'chart' }) .attr( @@ -171,13 +175,13 @@ export class CdfChartD3 { .ticks(3) .tickFormat(d => { if (Math.abs(d) < 1) { - return d3.format(".2")(d); + return d3.format('.2')(d); } else if (xMin > 1000 && xMax < 3000) { // Condition which identifies years; 2019, 2020, 2021. - return d3.format(".0")(d); + return d3.format('.0')(d); } else { - const prefix = d3.formatPrefix(".0", d); - return prefix(d).replace("G", "B"); + const prefix = d3.formatPrefix('.0', d); + return prefix(d).replace('G', 'B'); } }); } @@ -314,32 +318,77 @@ export class CdfChartD3 { .domain([yMinDomain, yMaxDomain]) .range([this.calc.chartHeight, 0]); - // Adds "g" for an y-axis. + // Adds 'g' for an y-axis. this.chart.append('g') .attr('class', 'lollipops-y-axis') .attr('transform', `translate(${this.calc.chartWidth}, 0)`) .call(d3.axisLeft(yScale)); + function showTooltip(d) { + d3.select('#lollipops-line-' + d.id) + .classed('lollipops-line-mouseover', true); + d3.select('#lollipops-circle-' + d.id) + .classed('lollipops-circle-mouseover', true) + .attr('r', 6); + tooltip.transition() + .style('opacity', .9); + tooltip.html(`X: ${d.x}, Y: ${d.y}`) + .style('left', (distributionChart.xScale(d.x) + 60) + 'px') + .style('top', yScale(d.y) + 'px'); + } + + function hideTooltip(d) { + d3.select('#lollipops-line-' + d.id) + .classed('lollipops-line-mouseover', false); + d3.select('#lollipops-circle-' + d.id) + .classed('lollipops-circle-mouseover', false) + .attr('r', 4); + tooltip.transition() + .style('opacity', 0); + } + // Lines. this.chart.selectAll('lollipops-line') .data(data) .enter() .append('line') .attr('class', 'lollipops-line') + .attr('id', d => 'lollipops-line-' + d.id) .attr('x1', d => distributionChart.xScale(d.x)) .attr('x2', d => distributionChart.xScale(d.x)) .attr('y1', d => yScale(d.y)) .attr('y2', yScale(0)); + // Define the div for the tooltip + const tooltip = this._container.append('div') + .attr('class', 'lollipop-tooltip') + .style('opacity', 0); + // Circles. this.chart.selectAll('lollipops-circle') .data(data) .enter() .append('circle') .attr('class', 'lollipops-circle') + .attr('id', d => 'lollipops-circle-' + d.id) .attr('cx', d => distributionChart.xScale(d.x)) .attr('cy', d => yScale(d.y)) .attr('r', '4'); + + // Rectangles. + this.chart.selectAll('lollipops-rectangle') + .data(data) + .enter() + .append('rect') + .attr('width', 30) + .attr('height', this.calc.chartHeight) + .attr('x', d => distributionChart.xScale(d.x) - 15) + .attr('y', 0) + .attr('opacity', 0) + .attr('pointer-events', 'all') + .on('mouseover', showTooltip) + .on('mouseout', hideTooltip) + ; } /** @@ -347,7 +396,7 @@ export class CdfChartD3 { * @returns {string} */ formatDates(ts) { - return moment(ts).format("MMMM Do YYYY"); + return moment(ts).format('MMMM Do YYYY'); } /** @@ -356,23 +405,23 @@ export class CdfChartD3 { */ getTimeTicksByStr(unit) { switch (unit) { - case "months": + case 'months': return d3.timeMonth.every(4); - case "quarters": + case 'quarters': return d3.timeMonth.every(3); - case "hours": + case 'hours': return d3.timeHour.every(10); - case "days": + case 'days': return d3.timeDay.every(7); - case "seconds": + case 'seconds': return d3.timeSecond.every(10); - case "years": + case 'years': return d3.timeYear.every(10); - case "minutes": + case 'minutes': return d3.timeMinute.every(10); - case "weeks": + case 'weeks': return d3.timeWeek.every(10); - case "milliseconds": + case 'milliseconds': return d3.timeMillisecond.every(10); default: return d3.timeYear.every(10); @@ -390,7 +439,7 @@ export class CdfChartD3 { const len = data.xs.length; for (let i = 0; i < len; i++) { - dt.push({ x: data.xs[i], y: data.ys[i] }); + dt.push({ x: data.xs[i], y: data.ys[i], id: i }); } return dt; diff --git a/src/components/charts/DistributionPlot/distPlotReact.js b/src/components/charts/DistributionPlot/distPlotReact.js index 8b5dd6e5..9bd9deee 100644 --- a/src/components/charts/DistributionPlot/distPlotReact.js +++ b/src/components/charts/DistributionPlot/distPlotReact.js @@ -14,6 +14,7 @@ function getRandomInt(min, max) { } /** + * @todo: To rename as "DistPlotReact". * @param props * @returns {*} * @constructor diff --git a/src/components/charts/DistributionPlot/styles.css b/src/components/charts/DistributionPlot/styles.css new file mode 100644 index 00000000..e47299f7 --- /dev/null +++ b/src/components/charts/DistributionPlot/styles.css @@ -0,0 +1,10 @@ +.lollipops-line-mouseover { + stroke-dasharray: 4; + animation: dash 2s linear infinite; +} + +@keyframes dash { + to { + stroke-dashoffset: 1000; + } +}