forked from carbon-design-system/carbon-charts
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ruler interaction replacing Grid mouse listeners for comparative tool…
…tips (carbon-design-system#499) * add ruler and axis tooltip * move ruler logic to it's own graphFrameComponent * refactor, add axis tooltip dynamic width, add matching elements hovering * use getDomainValue instead of hardcoded accessor * add snap to nearest point * use getSVGElementSize to get backdrop height * remove useless code * add trigger mouseout in hideRuler to only highlight single hovered element * WIP * fix categorical scale elements mouseover trigger * fix close points mouseover/out trigger * fix import order and package version * add more test data * add crosshair cursor when ruler is active to better select data points * refactor, more docs * fix axis tooltip baseline * minor refactor Co-Authored-By: Zvonimir Fras <[email protected]> * change ruler color * a11y bubble * only show ruler for continuous domain - wip * add support for top axis * no need to support discrete scales anymore * make sure tooltip does not go out of axis bbox * remove axis tooltip * only show ruler if there's match with data points * only show ruler on timescale linechart * add filter for items getting null values at times, breaking tooltip * refactor * fix(core): update import path carbon-components fix carbon-design-system#546 * v0.30.7 * Merge pull request carbon-design-system#529 from theiliad/tabular-data-format * add basic tabular data format support * string updates * finalize line graph implementation with tabular data support * cleanup * finalize scatter graph with support for tabular data * commented out ideas for the data format * time-series support for tabular data within line graph * scatter support * optimize logic to find main vertical & horizontal axes * remove primary & secondary labels * bubble chart support * grouped bar support * horizontal grouped bar * fix legend filtering * fix bubble and scatter * fix bar hover errors * simple bar support * simple bar time-series support * horizontal simple bar support * more graph support * fix pie tooltip * fix donut center value * fix line & scatter demo ranges * fix bar demos * stacked data supprot * backwards data format compatibility * fix plotting of datums with a value of 0 * fixed stacked bar tooltip * remove primary, secondary, useAsGraphDomain/Range * Update packages/core/src/model.ts Co-Authored-By: natashadecoste <[email protected]> * apply suggested PR changes * fix group bar positioning issues * apply PR suggested changes * Update packages/core/demo/data/bar.ts Co-Authored-By: natashadecoste <[email protected]> * Update packages/core/demo/data/bar.ts Co-Authored-By: natashadecoste <[email protected]> * Update packages/core/demo/data/bar.ts Co-Authored-By: natashadecoste <[email protected]> * rename "identifier" to "mapsTo" * add tutorials section to core storybook * add links to errors * remove unnecessary comment * add custom color scale support * apply PR suggested changes * fix tests Co-authored-by: natashadecoste <[email protected]> * v0.30.8 * Apply suggestions from code review Co-Authored-By: Eliad Moosavi <[email protected]> * applied suggestions from review * fix ruler css * Update packages/core/src/components/axes/ruler.ts Co-Authored-By: Eliad Moosavi <[email protected]> * Update packages/core/src/styles/components/_ruler.scss Co-Authored-By: Eliad Moosavi <[email protected]> * Update packages/core/src/components/axes/ruler.ts Co-Authored-By: Eliad Moosavi <[email protected]> * add suggestions from review * Update packages/core/demo/data/line.ts Co-Authored-By: natashadecoste <[email protected]> * Apply suggestions from code review Co-Authored-By: natashadecoste <[email protected]> * enable ruler on bubble and scatter * disable mouse listeners on grid * enable ruler for every scale type * fix ruler on linear scales, refactor matches logic Co-authored-by: Zvonimir Fras <[email protected]> Co-authored-by: Natasha DeCoste <[email protected]> Co-authored-by: carbon-bot <[email protected]> Co-authored-by: Eliad Moosavi <[email protected]>
- Loading branch information
1 parent
5c90302
commit 685666b
Showing
13 changed files
with
295 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Internal Imports | ||
import { Component } from "../component"; | ||
import { DOMUtils } from "../../services"; | ||
import { TooltipTypes, ScaleTypes } from "../../interfaces"; | ||
import { Tools } from "../../tools"; | ||
|
||
// D3 Imports | ||
import { mouse, Selection } from "d3-selection"; | ||
import { scaleLinear } from "d3-scale"; | ||
|
||
type GenericSvgSelection = Selection<SVGElement, any, SVGElement, any>; | ||
|
||
const THRESHOLD = 5; | ||
|
||
/** check if x is inside threshold area extents */ | ||
function pointIsWithinThreshold(dx: number, x: number) { | ||
return dx > x - THRESHOLD && dx < x + THRESHOLD; | ||
} | ||
|
||
/** | ||
* a compatibility function that accepts ordinal scales too | ||
* as those do not support .invert() by default, | ||
* so a scale clone is created to invert domain with range | ||
*/ | ||
function invertedScale(scale) { | ||
if (scale.invert) { | ||
return scale.invert; | ||
} | ||
|
||
return scaleLinear() | ||
.domain(scale.range()) | ||
.range(scale.domain()); | ||
} | ||
|
||
export class Ruler extends Component { | ||
type = "ruler"; | ||
backdrop: GenericSvgSelection; | ||
hoveredElements: GenericSvgSelection; | ||
|
||
render() { | ||
this.drawBackdrop(); | ||
this.addBackdropEventListeners(); | ||
} | ||
|
||
showRuler([x, y]: [number, number]) { | ||
const svg = this.parent; | ||
const ruler = DOMUtils.appendOrSelect(svg, "g.ruler"); | ||
const line = DOMUtils.appendOrSelect(ruler, "line.ruler-line"); | ||
const dataPointElements: GenericSvgSelection = svg.selectAll( | ||
"[role=graphics-symbol]" | ||
); | ||
const displayData = this.model.getDisplayData(); | ||
const domainScale = this.services.cartesianScales.getDomainScale(); | ||
const rangeScale = this.services.cartesianScales.getRangeScale(); | ||
const [yScaleEnd, yScaleStart] = rangeScale.range(); | ||
|
||
const scaledData: {domainValue: number, originalData: any}[] = displayData.map((d, i) => ({ | ||
domainValue: this.services.cartesianScales.getDomainValue(d, i), | ||
originalData: d | ||
})); | ||
|
||
/** | ||
* Find matches, reduce is used instead of filter | ||
* to only get elements which belong to the same axis coordinate | ||
*/ | ||
const dataPointsMatchingRulerLine: {domainValue: number, originalData: any}[] = | ||
scaledData.reduce((accum, currentValue) => { | ||
// store the first element of the accumulator array to compare it with current element being processed | ||
const sampleAccumValue = accum[0] ? accum[0].domainValue : undefined; | ||
|
||
// if accumulator is not empty and current value is bigger than already existing value in the accumulator, skip current iteration | ||
if (sampleAccumValue !== undefined && currentValue.domainValue > sampleAccumValue) { | ||
return accum; | ||
} | ||
|
||
// there's a match and currentValue is either less then or equal to already stored values | ||
if (pointIsWithinThreshold(currentValue.domainValue, x)) { | ||
if (sampleAccumValue !== undefined && currentValue < sampleAccumValue) { | ||
// there's a closer data point in the threshold area, so reinstantiate array | ||
accum = [currentValue]; | ||
} else { | ||
// currentValue is equal to already stored values, there's another match on the same coordinate | ||
accum.push(currentValue); | ||
} | ||
} | ||
|
||
return accum; | ||
}, []); | ||
|
||
// some data point match | ||
if (dataPointsMatchingRulerLine.length > 0) { | ||
const highlightItems = dataPointsMatchingRulerLine.map(d => d.originalData) | ||
.filter(d => { | ||
const rangeIdentifier = this.services.cartesianScales.getRangeIdentifier(); | ||
const value = d[rangeIdentifier]; | ||
return value !== null && value !== undefined; | ||
}); | ||
|
||
// get elements on which we should trigger mouse events | ||
const hoveredElements = dataPointElements.filter((d, i) => | ||
dataPointsMatchingRulerLine.includes(d) | ||
); | ||
|
||
/** if we pass from a trigger area to another one | ||
* mouseout on previous elements won't get dispatched | ||
* so we need to do it manually | ||
*/ | ||
if ( | ||
this.hoveredElements && | ||
this.hoveredElements.size() > 0 && | ||
!Tools.isEqual(this.hoveredElements, hoveredElements) | ||
) { | ||
this.hideRuler(); | ||
} | ||
|
||
hoveredElements.dispatch("mouseover"); | ||
|
||
// set current hovered elements | ||
this.hoveredElements = hoveredElements; | ||
|
||
this.services.events.dispatchEvent("show-tooltip", { | ||
hoveredElement: line, | ||
multidata: highlightItems, | ||
type: TooltipTypes.GRIDLINE | ||
}); | ||
|
||
ruler.attr("opacity", 1); | ||
|
||
// line snaps to matching point | ||
const sampleMatch = dataPointsMatchingRulerLine[0]; | ||
line.attr("y1", yScaleStart) | ||
.attr("y2", yScaleEnd) | ||
.attr("x1", sampleMatch.domainValue) | ||
.attr("x2", sampleMatch.domainValue); | ||
} else { | ||
ruler.attr("opacity", 0); | ||
dataPointElements.dispatch("mouseout"); | ||
} | ||
} | ||
|
||
hideRuler() { | ||
const svg = this.parent; | ||
const ruler = DOMUtils.appendOrSelect(svg, "g.ruler"); | ||
const dataPointElements = svg.selectAll("[role=graphics-symbol]"); | ||
|
||
dataPointElements.dispatch("mouseout"); | ||
ruler.attr("opacity", 0); | ||
} | ||
|
||
/** | ||
* Adds the listener on the X grid to trigger multiple point tooltips along the x axis. | ||
*/ | ||
addBackdropEventListeners() { | ||
const self = this; | ||
|
||
this.backdrop | ||
.on("mousemove mouseover", function() { | ||
const chartContainer = self.services.domUtils.getMainSVG(); | ||
const pos = mouse(chartContainer); | ||
|
||
self.showRuler(pos); | ||
}) | ||
.on("mouseout", function() { | ||
self.hideRuler(); | ||
self.services.events.dispatchEvent("hide-tooltip"); | ||
}); | ||
} | ||
|
||
drawBackdrop() { | ||
const svg = this.parent; | ||
|
||
const domainScale = this.services.cartesianScales.getDomainScale(); | ||
const rangeScale = this.services.cartesianScales.getRangeScale(); | ||
|
||
const [xScaleStart, xScaleEnd] = domainScale.range(); | ||
const [yScaleEnd, yScaleStart] = rangeScale.range(); | ||
|
||
// Get height from the grid | ||
this.backdrop = DOMUtils.appendOrSelect(svg, "svg.chart-grid-backdrop"); | ||
const backdropRect = DOMUtils.appendOrSelect( | ||
this.backdrop, | ||
"rect.chart-grid-backdrop" | ||
); | ||
|
||
this.backdrop | ||
.merge(backdropRect) | ||
.attr("x", xScaleStart) | ||
.attr("y", yScaleStart) | ||
.attr("width", xScaleEnd - xScaleStart) | ||
.attr("height", yScaleEnd - yScaleStart) | ||
.lower(); | ||
|
||
backdropRect.attr("width", "100%").attr("height", "100%"); | ||
} | ||
} |
Oops, something went wrong.