Merge branch 'master' of github.com:foretold-app/estiband
* 'master' of github.com:foretold-app/estiband: Fixes code style Adds a tooltip
This commit is contained in:
commit
6f3dd4b428
|
@ -86,6 +86,21 @@ module Styles = {
|
||||||
selector(".lollipops-x-axis .domain", [display(`none)]),
|
selector(".lollipops-x-axis .domain", [display(`none)]),
|
||||||
selector(".lollipops-x-axis .tick line", [display(`none)]),
|
selector(".lollipops-x-axis .tick line", [display(`none)]),
|
||||||
selector(".lollipops-x-axis .tick text", [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
|
showVerticalLine
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const d3 = require('d3');
|
const d3 = require('d3');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
|
require('./styles.css');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo: To rename as "DistPlotD3".
|
||||||
|
*/
|
||||||
export class CdfChartD3 {
|
export class CdfChartD3 {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -93,11 +97,11 @@ export class CdfChartD3 {
|
||||||
// Add svg.
|
// Add svg.
|
||||||
this.svg = this._container
|
this.svg = this._container
|
||||||
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
||||||
.attr('width', "100%")
|
.attr('width', '100%')
|
||||||
.attr('height', this.attrs.svgHeight)
|
.attr('height', this.attrs.svgHeight)
|
||||||
.attr('pointer-events', 'none');
|
.attr('pointer-events', 'none');
|
||||||
|
|
||||||
// Add container "g" (empty) element.
|
// Add container 'g' (empty) element.
|
||||||
this.chart = this.svg
|
this.chart = this.svg
|
||||||
.createObject({ tag: 'g', selector: 'chart' })
|
.createObject({ tag: 'g', selector: 'chart' })
|
||||||
.attr(
|
.attr(
|
||||||
|
@ -171,13 +175,13 @@ export class CdfChartD3 {
|
||||||
.ticks(3)
|
.ticks(3)
|
||||||
.tickFormat(d => {
|
.tickFormat(d => {
|
||||||
if (Math.abs(d) < 1) {
|
if (Math.abs(d) < 1) {
|
||||||
return d3.format(".2")(d);
|
return d3.format('.2')(d);
|
||||||
} else if (xMin > 1000 && xMax < 3000) {
|
} else if (xMin > 1000 && xMax < 3000) {
|
||||||
// Condition which identifies years; 2019, 2020, 2021.
|
// Condition which identifies years; 2019, 2020, 2021.
|
||||||
return d3.format(".0")(d);
|
return d3.format('.0')(d);
|
||||||
} else {
|
} else {
|
||||||
const prefix = d3.formatPrefix(".0", d);
|
const prefix = d3.formatPrefix('.0', d);
|
||||||
return prefix(d).replace("G", "B");
|
return prefix(d).replace('G', 'B');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -314,32 +318,77 @@ export class CdfChartD3 {
|
||||||
.domain([yMinDomain, yMaxDomain])
|
.domain([yMinDomain, yMaxDomain])
|
||||||
.range([this.calc.chartHeight, 0]);
|
.range([this.calc.chartHeight, 0]);
|
||||||
|
|
||||||
// Adds "g" for an y-axis.
|
// Adds 'g' for an y-axis.
|
||||||
this.chart.append('g')
|
this.chart.append('g')
|
||||||
.attr('class', 'lollipops-y-axis')
|
.attr('class', 'lollipops-y-axis')
|
||||||
.attr('transform', `translate(${this.calc.chartWidth}, 0)`)
|
.attr('transform', `translate(${this.calc.chartWidth}, 0)`)
|
||||||
.call(d3.axisLeft(yScale));
|
.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.
|
// Lines.
|
||||||
this.chart.selectAll('lollipops-line')
|
this.chart.selectAll('lollipops-line')
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append('line')
|
.append('line')
|
||||||
.attr('class', 'lollipops-line')
|
.attr('class', 'lollipops-line')
|
||||||
|
.attr('id', d => 'lollipops-line-' + d.id)
|
||||||
.attr('x1', d => distributionChart.xScale(d.x))
|
.attr('x1', d => distributionChart.xScale(d.x))
|
||||||
.attr('x2', d => distributionChart.xScale(d.x))
|
.attr('x2', d => distributionChart.xScale(d.x))
|
||||||
.attr('y1', d => yScale(d.y))
|
.attr('y1', d => yScale(d.y))
|
||||||
.attr('y2', yScale(0));
|
.attr('y2', yScale(0));
|
||||||
|
|
||||||
|
// Define the div for the tooltip
|
||||||
|
const tooltip = this._container.append('div')
|
||||||
|
.attr('class', 'lollipop-tooltip')
|
||||||
|
.style('opacity', 0);
|
||||||
|
|
||||||
// Circles.
|
// Circles.
|
||||||
this.chart.selectAll('lollipops-circle')
|
this.chart.selectAll('lollipops-circle')
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append('circle')
|
.append('circle')
|
||||||
.attr('class', 'lollipops-circle')
|
.attr('class', 'lollipops-circle')
|
||||||
|
.attr('id', d => 'lollipops-circle-' + d.id)
|
||||||
.attr('cx', d => distributionChart.xScale(d.x))
|
.attr('cx', d => distributionChart.xScale(d.x))
|
||||||
.attr('cy', d => yScale(d.y))
|
.attr('cy', d => yScale(d.y))
|
||||||
.attr('r', '4');
|
.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}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
formatDates(ts) {
|
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) {
|
getTimeTicksByStr(unit) {
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case "months":
|
case 'months':
|
||||||
return d3.timeMonth.every(4);
|
return d3.timeMonth.every(4);
|
||||||
case "quarters":
|
case 'quarters':
|
||||||
return d3.timeMonth.every(3);
|
return d3.timeMonth.every(3);
|
||||||
case "hours":
|
case 'hours':
|
||||||
return d3.timeHour.every(10);
|
return d3.timeHour.every(10);
|
||||||
case "days":
|
case 'days':
|
||||||
return d3.timeDay.every(7);
|
return d3.timeDay.every(7);
|
||||||
case "seconds":
|
case 'seconds':
|
||||||
return d3.timeSecond.every(10);
|
return d3.timeSecond.every(10);
|
||||||
case "years":
|
case 'years':
|
||||||
return d3.timeYear.every(10);
|
return d3.timeYear.every(10);
|
||||||
case "minutes":
|
case 'minutes':
|
||||||
return d3.timeMinute.every(10);
|
return d3.timeMinute.every(10);
|
||||||
case "weeks":
|
case 'weeks':
|
||||||
return d3.timeWeek.every(10);
|
return d3.timeWeek.every(10);
|
||||||
case "milliseconds":
|
case 'milliseconds':
|
||||||
return d3.timeMillisecond.every(10);
|
return d3.timeMillisecond.every(10);
|
||||||
default:
|
default:
|
||||||
return d3.timeYear.every(10);
|
return d3.timeYear.every(10);
|
||||||
|
@ -390,7 +439,7 @@ export class CdfChartD3 {
|
||||||
const len = data.xs.length;
|
const len = data.xs.length;
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
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;
|
return dt;
|
||||||
|
|
|
@ -14,6 +14,7 @@ function getRandomInt(min, max) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @todo: To rename as "DistPlotReact".
|
||||||
* @param props
|
* @param props
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
* @constructor
|
* @constructor
|
||||||
|
|
10
src/components/charts/DistributionPlot/styles.css
Normal file
10
src/components/charts/DistributionPlot/styles.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.lollipops-line-mouseover {
|
||||||
|
stroke-dasharray: 4;
|
||||||
|
animation: dash 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dash {
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 1000;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user