commit
ccf40c1639
|
@ -1,11 +1,19 @@
|
||||||
[@bs.module "./cdfChartReact.js"]
|
[@bs.module "./cdfChartReact.js"]
|
||||||
external cdfChart: ReasonReact.reactClass = "default";
|
external cdfChart: ReasonReact.reactClass = "default";
|
||||||
|
|
||||||
type primaryDistribution = {
|
type primaryDistribution =
|
||||||
.
|
option({
|
||||||
"xs": array(float),
|
.
|
||||||
"ys": array(float),
|
"xs": array(float),
|
||||||
};
|
"ys": array(float),
|
||||||
|
});
|
||||||
|
|
||||||
|
type discrete =
|
||||||
|
option({
|
||||||
|
.
|
||||||
|
"xs": array(float),
|
||||||
|
"ys": array(float),
|
||||||
|
});
|
||||||
|
|
||||||
[@react.component]
|
[@react.component]
|
||||||
let make =
|
let make =
|
||||||
|
@ -17,8 +25,10 @@ let make =
|
||||||
~minX=?,
|
~minX=?,
|
||||||
~onHover=(f: float) => (),
|
~onHover=(f: float) => (),
|
||||||
~primaryDistribution=?,
|
~primaryDistribution=?,
|
||||||
|
~discrete=?,
|
||||||
~scale=?,
|
~scale=?,
|
||||||
~showDistributionLines=?,
|
~showDistributionLines=?,
|
||||||
|
~showDistributionYAxis=?,
|
||||||
~showVerticalLine=?,
|
~showVerticalLine=?,
|
||||||
~timeScale=?,
|
~timeScale=?,
|
||||||
~verticalLine=?,
|
~verticalLine=?,
|
||||||
|
@ -35,8 +45,10 @@ let make =
|
||||||
~minX?,
|
~minX?,
|
||||||
~onHover,
|
~onHover,
|
||||||
~primaryDistribution?,
|
~primaryDistribution?,
|
||||||
|
~discrete?,
|
||||||
~scale?,
|
~scale?,
|
||||||
~showDistributionLines?,
|
~showDistributionLines?,
|
||||||
|
~showDistributionYAxis?,
|
||||||
~showVerticalLine?,
|
~showVerticalLine?,
|
||||||
~timeScale?,
|
~timeScale?,
|
||||||
~verticalLine?,
|
~verticalLine?,
|
||||||
|
|
|
@ -7,11 +7,19 @@ module Styles = {
|
||||||
let graph = chartColor =>
|
let graph = chartColor =>
|
||||||
style([
|
style([
|
||||||
position(`relative),
|
position(`relative),
|
||||||
selector(".axis", [fontSize(`px(9))]),
|
selector(".x-axis", [fontSize(`px(9))]),
|
||||||
selector(".domain", [display(`none)]),
|
selector(".x-axis .domain", [display(`none)]),
|
||||||
selector(".tick line", [display(`none)]),
|
selector(".x-axis .tick line", [display(`none)]),
|
||||||
selector(".tick text", [color(`hex("bfcad4"))]),
|
selector(".x-axis .tick text", [color(`hex("bfcad4"))]),
|
||||||
selector(".chart .area-path", [SVG.fill(chartColor)]),
|
selector(".chart .area-path", [SVG.fill(chartColor)]),
|
||||||
|
selector(".lollipops-line", [SVG.stroke(`hex("bfcad4"))]),
|
||||||
|
selector(
|
||||||
|
".lollipops-circle",
|
||||||
|
[SVG.stroke(`hex("bfcad4")), SVG.fill(`hex("bfcad4"))],
|
||||||
|
),
|
||||||
|
selector(".lollipops-x-axis .domain", [display(`none)]),
|
||||||
|
selector(".lollipops-x-axis .tick line", [display(`none)]),
|
||||||
|
selector(".lollipops-x-axis .tick text", [display(`none)]),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,12 +27,16 @@ module Styles = {
|
||||||
let make =
|
let make =
|
||||||
(
|
(
|
||||||
~color=`hex("111"),
|
~color=`hex("111"),
|
||||||
~data,
|
~discrete=?,
|
||||||
~height=200,
|
~height=200,
|
||||||
~maxX=?,
|
~maxX=?,
|
||||||
~minX=?,
|
~minX=?,
|
||||||
~onHover: float => unit,
|
~onHover: float => unit,
|
||||||
|
~primaryDistribution=?,
|
||||||
~scale=?,
|
~scale=?,
|
||||||
|
~showDistributionLines=false,
|
||||||
|
~showDistributionYAxis=false,
|
||||||
|
~showVerticalLine=false,
|
||||||
~timeScale=?,
|
~timeScale=?,
|
||||||
) => {
|
) => {
|
||||||
<div className={Styles.graph(color)}>
|
<div className={Styles.graph(color)}>
|
||||||
|
@ -33,13 +45,17 @@ let make =
|
||||||
?minX
|
?minX
|
||||||
?scale
|
?scale
|
||||||
?timeScale
|
?timeScale
|
||||||
|
discrete={discrete |> E.O.fmap(d => d |> Shape.Discrete.toJs)}
|
||||||
height
|
height
|
||||||
marginBottom=50
|
marginBottom=50
|
||||||
marginTop=0
|
marginTop=0
|
||||||
onHover
|
onHover
|
||||||
primaryDistribution={data |> Shape.XYShape.toJs}
|
primaryDistribution={
|
||||||
showDistributionLines=false
|
primaryDistribution |> E.O.fmap(pd => pd |> Shape.XYShape.toJs)
|
||||||
showVerticalLine=false
|
}
|
||||||
|
showDistributionLines
|
||||||
|
showDistributionYAxis
|
||||||
|
showVerticalLine
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
|
@ -7,10 +7,12 @@ module Mixed = {
|
||||||
React.useMemo1(
|
React.useMemo1(
|
||||||
() =>
|
() =>
|
||||||
<CdfChart__Plain
|
<CdfChart__Plain
|
||||||
data={data.continuous}
|
primaryDistribution={data.continuous}
|
||||||
|
discrete={data.discrete}
|
||||||
color={`hex("333")}
|
color={`hex("333")}
|
||||||
timeScale
|
timeScale
|
||||||
onHover={r => setX(_ => r)}
|
onHover={r => setX(_ => r)}
|
||||||
|
showDistributionYAxis=true
|
||||||
/>,
|
/>,
|
||||||
[|data|],
|
[|data|],
|
||||||
);
|
);
|
||||||
|
@ -68,7 +70,7 @@ module Cont = {
|
||||||
React.useMemo1(
|
React.useMemo1(
|
||||||
() =>
|
() =>
|
||||||
<CdfChart__Plain
|
<CdfChart__Plain
|
||||||
data=continuous
|
primaryDistribution=continuous
|
||||||
color={`hex("333")}
|
color={`hex("333")}
|
||||||
onHover
|
onHover
|
||||||
timeScale
|
timeScale
|
||||||
|
|
|
@ -20,19 +20,30 @@ export class CdfChartD3 {
|
||||||
scale: 'linear',
|
scale: 'linear',
|
||||||
timeScale: null,
|
timeScale: null,
|
||||||
showDistributionLines: true,
|
showDistributionLines: true,
|
||||||
|
showDistributionYAxis: false,
|
||||||
areaColors: ['#E1E5EC', '#E1E5EC'],
|
areaColors: ['#E1E5EC', '#E1E5EC'],
|
||||||
logBase: 10,
|
logBase: 10,
|
||||||
verticalLine: 110,
|
verticalLine: 110,
|
||||||
showVerticalLine: true,
|
showVerticalLine: true,
|
||||||
data: null,
|
data: {
|
||||||
|
primary: null,
|
||||||
|
discrete: null,
|
||||||
|
},
|
||||||
onHover: (e) => {
|
onHover: (e) => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.hoverLine = null;
|
|
||||||
this.xScale = null;
|
this.calc = {
|
||||||
this.dataPoints = null;
|
chartLeftMargin: null,
|
||||||
this.mouseover = this.mouseover.bind(this);
|
chartTopMargin: null,
|
||||||
this.mouseout = this.mouseout.bind(this);
|
chartWidth: null,
|
||||||
|
chartHeight: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chart = null;
|
||||||
|
this.svg = null;
|
||||||
|
this._container = null;
|
||||||
|
|
||||||
this.formatDates = this.formatDates.bind(this);
|
this.formatDates = this.formatDates.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +107,11 @@ export class CdfChartD3 {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showDistributionYAxis(showDistributionYAxis) {
|
||||||
|
this.attrs.showDistributionYAxis = showDistributionYAxis;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
verticalLine(verticalLine) {
|
verticalLine(verticalLine) {
|
||||||
this.attrs.verticalLine = verticalLine;
|
this.attrs.verticalLine = verticalLine;
|
||||||
return this;
|
return this;
|
||||||
|
@ -116,69 +132,77 @@ export class CdfChartD3 {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param key
|
|
||||||
* @returns {[]}
|
|
||||||
*/
|
|
||||||
getDataPoints(key) {
|
|
||||||
const dt = [];
|
|
||||||
const data = this.attrs.data[key];
|
|
||||||
const len = data.xs.length;
|
|
||||||
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
dt.push({ x: data.xs[i], y: data.ys[i] });
|
|
||||||
}
|
|
||||||
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const attrs = this.attrs;
|
this._container = d3.select(this.attrs.container);
|
||||||
const container = d3.select(attrs.container);
|
if (this._container.node() === null) {
|
||||||
if (container.node() === null) {
|
|
||||||
console.error('Container for D3 is not defined.');
|
console.error('Container for D3 is not defined.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the width from the DOM element.
|
// Sets the width from the DOM element.
|
||||||
const containerRect = container.node().getBoundingClientRect();
|
const containerRect = this._container.node().getBoundingClientRect();
|
||||||
if (containerRect.width > 0) {
|
if (containerRect.width > 0) {
|
||||||
attrs.svgWidth = containerRect.width;
|
this.attrs.svgWidth = containerRect.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculated properties.
|
// Calculated properties.
|
||||||
const calc = {};
|
this.calc.chartLeftMargin = this.attrs.marginLeft;
|
||||||
calc.chartLeftMargin = attrs.marginLeft;
|
this.calc.chartTopMargin = this.attrs.marginTop;
|
||||||
calc.chartTopMargin = attrs.marginTop;
|
this.calc.chartWidth = this.attrs.svgWidth - this.attrs.marginRight - this.attrs.marginLeft;
|
||||||
calc.chartWidth = attrs.svgWidth - attrs.marginRight - attrs.marginLeft;
|
this.calc.chartHeight = this.attrs.svgHeight - this.attrs.marginBottom - this.attrs.marginTop;
|
||||||
calc.chartHeight = attrs.svgHeight - attrs.marginBottom - attrs.marginTop;
|
|
||||||
|
|
||||||
const areaColorRange = d3.scaleOrdinal().range(attrs.areaColors);
|
// Add svg.
|
||||||
this.dataPoints = [this.getDataPoints('primary')];
|
this.svg = this._container
|
||||||
|
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
||||||
|
.attr('width', "100%")
|
||||||
|
.attr('height', this.attrs.svgHeight)
|
||||||
|
.attr('pointer-events', 'none');
|
||||||
|
|
||||||
|
// Add container g element.
|
||||||
|
this.chart = this.svg
|
||||||
|
.createObject({ tag: 'g', selector: 'chart' })
|
||||||
|
.attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' + this.calc.chartLeftMargin + ',' + this.calc.chartTopMargin + ')',
|
||||||
|
);
|
||||||
|
|
||||||
|
if(this.hasDate('primary')){
|
||||||
|
const distributionChart = this.addDistributionChart();
|
||||||
|
if(this.hasDate('discrete')) {
|
||||||
|
this.addLollipopsChart(distributionChart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDistributionChart() {
|
||||||
|
const areaColorRange = d3.scaleOrdinal().range(this.attrs.areaColors);
|
||||||
|
const dataPoints = [this.getDataPoints('primary')];
|
||||||
|
|
||||||
|
// Boundaries.
|
||||||
|
const xMin = this.attrs.minX || d3.min(this.attrs.data.primary.xs);
|
||||||
|
const xMax = this.attrs.maxX || d3.max(this.attrs.data.primary.xs);
|
||||||
|
const yMin = d3.min(this.attrs.data.primary.ys);
|
||||||
|
const yMax = d3.max(this.attrs.data.primary.ys);
|
||||||
|
|
||||||
// Scales.
|
// Scales.
|
||||||
const xMin = d3.min(attrs.data.primary.xs);
|
let xScale = null;
|
||||||
const xMax = d3.max(attrs.data.primary.xs);
|
if (this.attrs.scale === 'linear') {
|
||||||
|
xScale = d3.scaleLinear()
|
||||||
if (attrs.scale === 'linear') {
|
.domain([xMin, xMax])
|
||||||
this.xScale = d3.scaleLinear()
|
.range([0, this.calc.chartWidth]);
|
||||||
.domain([attrs.minX || xMin, attrs.maxX || xMax])
|
|
||||||
.range([0, calc.chartWidth]);
|
|
||||||
} else {
|
} else {
|
||||||
this.xScale = d3.scaleLog()
|
xScale = d3.scaleLog()
|
||||||
.base(attrs.logBase)
|
.base(this.attrs.logBase)
|
||||||
.domain([attrs.minX, attrs.maxX])
|
.domain([xMin, xMax])
|
||||||
.range([0, calc.chartWidth]);
|
.range([0, this.calc.chartWidth]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const yMin = d3.min(attrs.data.primary.ys);
|
const yScale = d3.scaleLinear()
|
||||||
const yMax = d3.max(attrs.data.primary.ys);
|
|
||||||
|
|
||||||
this.yScale = d3.scaleLinear()
|
|
||||||
.domain([yMin, yMax])
|
.domain([yMin, yMax])
|
||||||
.range([calc.chartHeight, 0]);
|
.range([this.calc.chartHeight, 0]);
|
||||||
|
|
||||||
// Axis generator.
|
// Axis generator.
|
||||||
|
let xAxis = null;
|
||||||
if (!!this.attrs.timeScale) {
|
if (!!this.attrs.timeScale) {
|
||||||
const zero = _.get(this.attrs.timeScale, 'zero', moment());
|
const zero = _.get(this.attrs.timeScale, 'zero', moment());
|
||||||
const unit = _.get(this.attrs.timeScale, 'unit', 'years');
|
const unit = _.get(this.attrs.timeScale, 'unit', 'years');
|
||||||
|
@ -189,14 +213,14 @@ export class CdfChartD3 {
|
||||||
const xScaleTime = d3.scaleTime()
|
const xScaleTime = d3.scaleTime()
|
||||||
.domain([left.toDate(), right.toDate()])
|
.domain([left.toDate(), right.toDate()])
|
||||||
.nice()
|
.nice()
|
||||||
.range([0, calc.chartWidth]);
|
.range([0, this.calc.chartWidth]);
|
||||||
|
|
||||||
this.xAxis = d3.axisBottom()
|
xAxis = d3.axisBottom()
|
||||||
.scale(xScaleTime)
|
.scale(xScaleTime)
|
||||||
.ticks(this.getTimeTicksByStr(unit))
|
.ticks(this.getTimeTicksByStr(unit))
|
||||||
.tickFormat(this.formatDates);
|
.tickFormat(this.formatDates);
|
||||||
} else {
|
} else {
|
||||||
this.xAxis = d3.axisBottom(this.xScale)
|
xAxis = d3.axisBottom(xScale)
|
||||||
.ticks(3)
|
.ticks(3)
|
||||||
.tickFormat(d => {
|
.tickFormat(d => {
|
||||||
if (Math.abs(d) < 1) {
|
if (Math.abs(d) < 1) {
|
||||||
|
@ -211,54 +235,46 @@ export class CdfChartD3 {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const yAxis = d3.axisRight(yScale);
|
||||||
|
|
||||||
// Objects.
|
// Objects.
|
||||||
const line = d3.line()
|
const line = d3.line()
|
||||||
.x(d => this.xScale(d.x))
|
.x(d => xScale(d.x))
|
||||||
.y(d => this.yScale(d.y));
|
.y(d => yScale(d.y));
|
||||||
|
|
||||||
const area = d3.area()
|
const area = d3.area()
|
||||||
.x(d => this.xScale(d.x))
|
.x(d => xScale(d.x))
|
||||||
.y1(d => this.yScale(d.y))
|
.y1(d => yScale(d.y))
|
||||||
.y0(calc.chartHeight);
|
.y0(this.calc.chartHeight);
|
||||||
|
|
||||||
// Add svg.
|
|
||||||
const svg = container
|
|
||||||
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
|
||||||
.attr('width', "100%")
|
|
||||||
.attr('height', attrs.svgHeight)
|
|
||||||
.attr('pointer-events', 'none');
|
|
||||||
|
|
||||||
// Add container g element.
|
|
||||||
this.chart = svg
|
|
||||||
.createObject({ tag: 'g', selector: 'chart' })
|
|
||||||
.attr(
|
|
||||||
'transform',
|
|
||||||
'translate(' + calc.chartLeftMargin + ',' + calc.chartTopMargin + ')',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add axis.
|
// Add axis.
|
||||||
this.chart.createObject({ tag: 'g', selector: 'axis' })
|
this.chart.createObject({ tag: 'g', selector: 'x-axis' })
|
||||||
.attr('transform', 'translate(' + 0 + ',' + calc.chartHeight + ')')
|
.attr('transform', 'translate(0,' + this.calc.chartHeight + ')')
|
||||||
.call(this.xAxis);
|
.call(xAxis);
|
||||||
|
|
||||||
|
if (this.attrs.showDistributionYAxis) {
|
||||||
|
this.chart.createObject({ tag: 'g', selector: 'y-axis' })
|
||||||
|
.call(yAxis);
|
||||||
|
}
|
||||||
|
|
||||||
// Draw area.
|
// Draw area.
|
||||||
this.chart
|
this.chart
|
||||||
.createObjectsWithData({
|
.createObjectsWithData({
|
||||||
tag: 'path',
|
tag: 'path',
|
||||||
selector: 'area-path',
|
selector: 'area-path',
|
||||||
data: this.dataPoints,
|
data: dataPoints,
|
||||||
})
|
})
|
||||||
.attr('d', area)
|
.attr('d', area)
|
||||||
.attr('fill', (d, i) => areaColorRange(i))
|
.attr('fill', (d, i) => areaColorRange(i))
|
||||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 0.5);
|
.attr('opacity', (d, i) => i === 0 ? 0.7 : 0.5);
|
||||||
|
|
||||||
// Draw line.
|
// Draw line.
|
||||||
if (attrs.showDistributionLines) {
|
if (this.attrs.showDistributionLines) {
|
||||||
this.chart
|
this.chart
|
||||||
.createObjectsWithData({
|
.createObjectsWithData({
|
||||||
tag: 'path',
|
tag: 'path',
|
||||||
selector: 'line-path',
|
selector: 'line-path',
|
||||||
data: this.dataPoints,
|
data: dataPoints,
|
||||||
})
|
})
|
||||||
.attr('d', line)
|
.attr('d', line)
|
||||||
.attr('id', (d, i) => 'line-' + (i + 1))
|
.attr('id', (d, i) => 'line-' + (i + 1))
|
||||||
|
@ -266,74 +282,109 @@ export class CdfChartD3 {
|
||||||
.attr('fill', 'none');
|
.attr('fill', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attrs.showVerticalLine) {
|
if (this.attrs.showVerticalLine) {
|
||||||
this.chart
|
this.chart
|
||||||
.createObject({ tag: 'line', selector: 'v-line' })
|
.createObject({ tag: 'line', selector: 'v-line' })
|
||||||
.attr('x1', this.xScale(attrs.verticalLine))
|
.attr('x1', xScale(this.attrs.verticalLine))
|
||||||
.attr('x2', this.xScale(attrs.verticalLine))
|
.attr('x2', xScale(this.attrs.verticalLine))
|
||||||
.attr('y1', 0)
|
.attr('y1', 0)
|
||||||
.attr('y2', calc.chartHeight)
|
.attr('y2', this.calc.chartHeight)
|
||||||
.attr('stroke-width', 1.5)
|
.attr('stroke-width', 1.5)
|
||||||
.attr('stroke-dasharray', '6 6')
|
.attr('stroke-dasharray', '6 6')
|
||||||
.attr('stroke', 'steelblue');
|
.attr('stroke', 'steelblue');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hoverLine = this.chart
|
const hoverLine = this.chart
|
||||||
.createObject({ tag: 'line', selector: 'hover-line' })
|
.createObject({ tag: 'line', selector: 'hover-line' })
|
||||||
.attr('x1', 0)
|
.attr('x1', 0)
|
||||||
.attr('x2', 0)
|
.attr('x2', 0)
|
||||||
.attr('y1', 0)
|
.attr('y1', 0)
|
||||||
.attr('y2', calc.chartHeight)
|
.attr('y2', this.calc.chartHeight)
|
||||||
.attr('opacity', 0)
|
.attr('opacity', 0)
|
||||||
.attr('stroke-width', 1.5)
|
.attr('stroke-width', 1.5)
|
||||||
.attr('stroke-dasharray', '6 6')
|
.attr('stroke-dasharray', '6 6')
|
||||||
.attr('stroke', '#22313F');
|
.attr('stroke', '#22313F');
|
||||||
|
|
||||||
// Add drawing rectangle.
|
// Add drawing rectangle.
|
||||||
const thi$ = this;
|
{
|
||||||
this.chart
|
const context = this;
|
||||||
.createObject({ tag: 'rect', selector: 'mouse-rect' })
|
const range = [
|
||||||
.attr('width', calc.chartWidth)
|
xScale(dataPoints[dataPoints.length - 1][0].x),
|
||||||
.attr('height', calc.chartHeight)
|
xScale(
|
||||||
.attr('fill', 'transparent')
|
dataPoints
|
||||||
.attr('pointer-events', 'all')
|
[dataPoints.length - 1]
|
||||||
.on('mouseover', function () {
|
[dataPoints[dataPoints.length - 1].length - 1].x,
|
||||||
thi$.mouseover(this);
|
),
|
||||||
})
|
];
|
||||||
.on('mousemove', function () {
|
|
||||||
thi$.mouseover(this);
|
|
||||||
})
|
|
||||||
.on('mouseout', this.mouseout);
|
|
||||||
|
|
||||||
return this;
|
function mouseover() {
|
||||||
}
|
const mouse = d3.mouse(this);
|
||||||
|
hoverLine.attr('opacity', 1).attr('x1', mouse[0]).attr('x2', mouse[0]);
|
||||||
|
const xValue = mouse[0] > range[0] && mouse[0] < range[1]
|
||||||
|
? xScale.invert(mouse[0]).toFixed(2)
|
||||||
|
: 0;
|
||||||
|
context.attrs.onHover(xValue);
|
||||||
|
}
|
||||||
|
|
||||||
mouseover(constructor) {
|
function mouseout() {
|
||||||
const mouse = d3.mouse(constructor);
|
hoverLine.attr('opacity', 0)
|
||||||
this.hoverLine.attr('opacity', 1)
|
}
|
||||||
.attr('x1', mouse[0])
|
|
||||||
.attr('x2', mouse[0]);
|
|
||||||
|
|
||||||
const xValue = this.xScale.invert(mouse[0]).toFixed(2);
|
this.chart
|
||||||
|
.createObject({ tag: 'rect', selector: 'mouse-rect' })
|
||||||
const range = [
|
.attr('width', this.calc.chartWidth)
|
||||||
this.xScale(this.dataPoints[this.dataPoints.length - 1][0].x),
|
.attr('height', this.calc.chartHeight)
|
||||||
this.xScale(
|
.attr('fill', 'transparent')
|
||||||
this.dataPoints
|
.attr('pointer-events', 'all')
|
||||||
[this.dataPoints.length - 1]
|
.on('mouseover', mouseover)
|
||||||
[this.dataPoints[this.dataPoints.length - 1].length - 1].x,
|
.on('mousemove', mouseover)
|
||||||
),
|
.on('mouseout', mouseout);
|
||||||
];
|
|
||||||
|
|
||||||
if (mouse[0] > range[0] && mouse[0] < range[1]) {
|
|
||||||
this.attrs.onHover(xValue);
|
|
||||||
} else {
|
|
||||||
this.attrs.onHover(0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { xScale, yScale };
|
||||||
}
|
}
|
||||||
|
|
||||||
mouseout() {
|
addLollipopsChart(distributionChart) {
|
||||||
this.hoverLine.attr('opacity', 0)
|
const data = this.getDataPoints('discrete');
|
||||||
|
const ys = data.map(item => item.y);
|
||||||
|
const yMax = d3.max(ys);
|
||||||
|
|
||||||
|
// X axis
|
||||||
|
this.chart.append("g")
|
||||||
|
.attr("class", 'lollipops-x-axis')
|
||||||
|
.attr("transform", "translate(0," + this.calc.chartHeight + ")")
|
||||||
|
.call(d3.axisBottom(distributionChart.xScale));
|
||||||
|
|
||||||
|
// Y axis
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain([0, yMax])
|
||||||
|
.range([this.calc.chartHeight, 0]);
|
||||||
|
|
||||||
|
this.chart.append("g")
|
||||||
|
.attr("class", 'lollipops-y-axis')
|
||||||
|
.attr("transform", "translate(" + this.calc.chartWidth + ",0)")
|
||||||
|
.call(d3.axisLeft(yScale));
|
||||||
|
|
||||||
|
// Lines
|
||||||
|
this.chart.selectAll("lollipops-line")
|
||||||
|
.data(data)
|
||||||
|
.enter()
|
||||||
|
.append("line")
|
||||||
|
.attr("class", 'lollipops-line')
|
||||||
|
.attr("x1", d => distributionChart.xScale(d.x))
|
||||||
|
.attr("x2", d => distributionChart.xScale(d.x))
|
||||||
|
.attr("y1", d => yScale(d.y))
|
||||||
|
.attr("y2", yScale(0));
|
||||||
|
|
||||||
|
// Circles
|
||||||
|
this.chart.selectAll("lollipops-circle")
|
||||||
|
.data(data)
|
||||||
|
.enter()
|
||||||
|
.append("circle")
|
||||||
|
.attr("class", 'lollipops-circle')
|
||||||
|
.attr("cx", d => distributionChart.xScale(d.x))
|
||||||
|
.attr("cy", d => yScale(d.y))
|
||||||
|
.attr("r", "4");
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDates(ts) {
|
formatDates(ts) {
|
||||||
|
@ -368,11 +419,39 @@ export class CdfChartD3 {
|
||||||
return d3.timeYear.every(10);
|
return d3.timeYear.every(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {name} key
|
||||||
|
* @returns {{x: number[], y: number[]}}
|
||||||
|
*/
|
||||||
|
getDataPoints(key) {
|
||||||
|
const dt = [];
|
||||||
|
const emptyShape = { xs: [], ys: [] };
|
||||||
|
const data = _.get(this.attrs.data, key, emptyShape);
|
||||||
|
const len = data.xs.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
dt.push({ x: data.xs[i], y: data.ys[i] });
|
||||||
|
}
|
||||||
|
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasDate(key) {
|
||||||
|
const data = _.get(this.attrs.data, key);
|
||||||
|
return !!data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @docs: https://github.com/d3/d3-selection
|
* @docs: https://github.com/d3/d3-selection
|
||||||
* @param params
|
* @param {object} params
|
||||||
|
* @param {string} params.selector
|
||||||
|
* @param {string} params.tag
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
d3.selection.prototype.createObject = function createObject(params) {
|
d3.selection.prototype.createObject = function createObject(params) {
|
||||||
|
@ -382,16 +461,11 @@ d3.selection.prototype.createObject = function createObject(params) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @example:
|
|
||||||
* This call example
|
|
||||||
* createObjectsByData({
|
|
||||||
* tag: 'path',
|
|
||||||
* selector: 'line-path',
|
|
||||||
* data: this.dataPoints,
|
|
||||||
* })
|
|
||||||
* will create a new tag "<path class="line-path">1,2,3</path>".
|
|
||||||
* @docs: https://github.com/d3/d3-selection
|
* @docs: https://github.com/d3/d3-selection
|
||||||
* @param params
|
* @param {object} params
|
||||||
|
* @param {string} params.selector
|
||||||
|
* @param {string} params.tag
|
||||||
|
* @param {*[]} params.data
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
d3.selection.prototype.createObjectsWithData = function createObjectsWithData(params) {
|
d3.selection.prototype.createObjectsWithData = function createObjectsWithData(params) {
|
||||||
|
|
|
@ -45,10 +45,14 @@ function CdfChartReact(props) {
|
||||||
.marginRight(5)
|
.marginRight(5)
|
||||||
.marginTop(5)
|
.marginTop(5)
|
||||||
.showDistributionLines(props.showDistributionLines)
|
.showDistributionLines(props.showDistributionLines)
|
||||||
|
.showDistributionYAxis(props.showDistributionYAxis)
|
||||||
.verticalLine(props.verticalLine)
|
.verticalLine(props.verticalLine)
|
||||||
.showVerticalLine(props.showVerticalLine)
|
.showVerticalLine(props.showVerticalLine)
|
||||||
.container(containerRef.current)
|
.container(containerRef.current)
|
||||||
.data({ primary: props.primaryDistribution })
|
.data({
|
||||||
|
primary: props.primaryDistribution,
|
||||||
|
discrete: props.discrete,
|
||||||
|
})
|
||||||
.scale(scale)
|
.scale(scale)
|
||||||
.timeScale(props.timeScale)
|
.timeScale(props.timeScale)
|
||||||
.render();
|
.render();
|
||||||
|
|
|
@ -2,11 +2,19 @@ module Styles = {
|
||||||
open Css;
|
open Css;
|
||||||
let graph = chartColor =>
|
let graph = chartColor =>
|
||||||
style([
|
style([
|
||||||
selector(".axis", [fontSize(`px(9))]),
|
selector(".x-axis", [fontSize(`px(9))]),
|
||||||
selector(".domain", [display(`none)]),
|
selector(".x-axis .domain", [display(`none)]),
|
||||||
selector(".tick line", [display(`none)]),
|
selector(".x-axis .tick line", [display(`none)]),
|
||||||
selector(".tick text", [color(`hex("bfcad4"))]),
|
selector(".x-axis .tick text", [color(`hex("bfcad4"))]),
|
||||||
selector(".chart .area-path", [SVG.fill(chartColor)]),
|
selector(".chart .area-path", [SVG.fill(chartColor)]),
|
||||||
|
selector(".lollipops-line", [SVG.stroke(`hex("bfcad4"))]),
|
||||||
|
selector(
|
||||||
|
".lollipops-circle",
|
||||||
|
[SVG.stroke(`hex("bfcad4")), SVG.fill(`hex("bfcad4"))],
|
||||||
|
),
|
||||||
|
selector(".lollipops-x-axis .domain", [display(`none)]),
|
||||||
|
selector(".lollipops-x-axis .tick line", [display(`none)]),
|
||||||
|
selector(".lollipops-x-axis .tick text", [display(`none)]),
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user