Factor the tooltip out of line-chart and into a primitive
This commit is contained in:
parent
391aef9c4d
commit
498d9d2b36
|
@ -0,0 +1,7 @@
|
|||
<div class="chart-tooltip {{if @active "active" "inactive"}}" style={{@style}} ...attributes>
|
||||
<ol>
|
||||
{{#each @data as |props|}}
|
||||
{{yield props.series props.datum (inc props.index)}}
|
||||
{{/each}}
|
||||
</ol>
|
||||
</div>
|
|
@ -68,6 +68,7 @@ export default class LineChart extends Component {
|
|||
@tracked height = 0;
|
||||
@tracked isActive = false;
|
||||
@tracked activeDatum = null;
|
||||
@tracked activeData = [];
|
||||
@tracked tooltipPosition = null;
|
||||
@tracked element = null;
|
||||
@tracked ready = false;
|
||||
|
@ -251,37 +252,65 @@ export default class LineChart extends Component {
|
|||
canvas.on('mouseleave', () => {
|
||||
run.schedule('afterRender', this, () => (this.isActive = false));
|
||||
this.activeDatum = null;
|
||||
this.activeData = [];
|
||||
});
|
||||
}
|
||||
|
||||
updateActiveDatum(mouseX) {
|
||||
const { xScale, xProp, yScale, yProp, data } = this;
|
||||
if (!this.data || !this.data.length) return;
|
||||
|
||||
if (!data || !data.length) return;
|
||||
const { xScale, xProp, yScale, yProp } = this;
|
||||
let { dataProp, data } = this.args;
|
||||
|
||||
// Map the mouse coordinate to the index in the data array
|
||||
const bisector = d3Array.bisector(d => d[xProp]).left;
|
||||
const x = xScale.invert(mouseX);
|
||||
const index = bisector(data, x, 1);
|
||||
|
||||
// The data point on either side of the cursor
|
||||
const dLeft = data[index - 1];
|
||||
const dRight = data[index];
|
||||
|
||||
let datum;
|
||||
|
||||
// If there is only one point, it's the activeDatum
|
||||
if (dLeft && !dRight) {
|
||||
datum = dLeft;
|
||||
} else {
|
||||
// Pick the closer point
|
||||
datum = x - dLeft[xProp] > dRight[xProp] - x ? dRight : dLeft;
|
||||
if (!dataProp) {
|
||||
dataProp = 'data';
|
||||
data = [{ data: this.data }];
|
||||
}
|
||||
|
||||
this.activeDatum = datum;
|
||||
// Map screen coordinates to data domain
|
||||
const bisector = d3Array.bisector(d => d[xProp]).left;
|
||||
const x = xScale.invert(mouseX);
|
||||
|
||||
// Find the closest datum to the cursor for each series
|
||||
const activeData = data.map((series, seriesIndex) => {
|
||||
const dataset = series[dataProp];
|
||||
const index = bisector(dataset, x, 1);
|
||||
|
||||
// The data point on either side of the cursor
|
||||
const dLeft = dataset[index - 1];
|
||||
const dRight = dataset[index];
|
||||
|
||||
let datum;
|
||||
|
||||
// If there is only one point, it's the activeDatum
|
||||
if (dLeft && !dRight) {
|
||||
datum = dLeft;
|
||||
} else {
|
||||
// Pick the closer point
|
||||
datum = x - dLeft[xProp] > dRight[xProp] - x ? dRight : dLeft;
|
||||
}
|
||||
|
||||
// TODO: Preformat numbers
|
||||
return { series, datum, index: seriesIndex };
|
||||
});
|
||||
|
||||
// Of the selected data, determine which is closest
|
||||
const closestDatum = activeData.sort(
|
||||
(a, b) => Math.abs(a.datum[xProp] - x) - Math.abs(b.datum[xProp] - x)
|
||||
)[0];
|
||||
|
||||
// If any other selected data are beyond a distance threshold, drop them from the list
|
||||
// xScale is used here to measure distance in screen-space rather than data-space.
|
||||
const dist = Math.abs(xScale(closestDatum.datum[xProp]) - mouseX);
|
||||
const filteredData = activeData.filter(
|
||||
d => Math.abs(xScale(d.datum[xProp]) - mouseX) < dist + 10
|
||||
);
|
||||
|
||||
this.activeData = filteredData;
|
||||
this.activeDatum = closestDatum.datum;
|
||||
this.tooltipPosition = {
|
||||
left: xScale(datum[xProp]),
|
||||
top: yScale(datum[yProp]) - 10,
|
||||
left: xScale(this.activeDatum[xProp]),
|
||||
top: yScale(this.activeDatum[yProp]) - 10,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -45,15 +45,21 @@
|
|||
prop=this.yProp
|
||||
left=this.canvasDimensions.left
|
||||
width=this.canvasDimensions.width)
|
||||
Tooltip=(component "chart-primitives/tooltip"
|
||||
active=this.activeData.length
|
||||
style=this.tooltipStyle
|
||||
data=this.activeData)
|
||||
) to="after"}}
|
||||
{{/if}}
|
||||
<div class="chart-tooltip is-snappy {{if this.isActive "active" "inactive"}}" style={{this.tooltipStyle}}>
|
||||
<p>
|
||||
<span class="label">
|
||||
<span class="color-swatch {{this.chartClass}}" />
|
||||
{{this.activeDatumLabel}}
|
||||
</span>
|
||||
<span class="value">{{this.activeDatumValue}}</span>
|
||||
</p>
|
||||
</div>
|
||||
{{#unless @dataProp}}
|
||||
<div class="chart-tooltip is-snappy {{if this.isActive "active" "inactive"}}" style={{this.tooltipStyle}}>
|
||||
<p>
|
||||
<span class="label">
|
||||
<span class="color-swatch {{this.chartClass}}" />
|
||||
{{this.activeDatumLabel}}
|
||||
</span>
|
||||
<span class="value">{{this.activeDatumValue}}</span>
|
||||
</p>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
|
|
@ -370,6 +370,14 @@ export let MultiLine = () => ({
|
|||
<c.Area @data={{series.data}} @colorScale="reds" @index={{idx}} />
|
||||
{{/each}}
|
||||
</:svg>
|
||||
<:after as |c|>
|
||||
<c.Tooltip class="is-snappy" as |series datum index|>
|
||||
<li>
|
||||
<span class="label"><span class="color-swatch swatch-reds swatch-reds-{{index}}" />{{series.name}}</span>
|
||||
<span class="value">{{datum.y}}</span>
|
||||
</li>
|
||||
</c.Tooltip>
|
||||
</:after>
|
||||
</LineChart>
|
||||
<p>{{this.activeAnnotation.info}}</p>
|
||||
{{/if}}
|
||||
|
@ -379,18 +387,6 @@ export let MultiLine = () => ({
|
|||
data: DelayedArray.create([
|
||||
{
|
||||
name: 'Series 1',
|
||||
data: [
|
||||
{ x: 1, y: 5 },
|
||||
{ x: 2, y: 1 },
|
||||
{ x: 3, y: 2 },
|
||||
{ x: 4, y: 2 },
|
||||
{ x: 5, y: 9 },
|
||||
{ x: 6, y: 3 },
|
||||
{ x: 7, y: 4 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Series 2',
|
||||
data: [
|
||||
{ x: 3, y: 7 },
|
||||
{ x: 4, y: 5 },
|
||||
|
@ -401,6 +397,18 @@ export let MultiLine = () => ({
|
|||
{ x: 9, y: 6 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Series 2',
|
||||
data: [
|
||||
{ x: 1, y: 5 },
|
||||
{ x: 2, y: 1 },
|
||||
{ x: 3, y: 2 },
|
||||
{ x: 4, y: 2 },
|
||||
{ x: 5, y: 9 },
|
||||
{ x: 6, y: 3 },
|
||||
{ x: 7, y: 4 },
|
||||
],
|
||||
},
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue