Skip to content

Commit

Permalink
feat(axis): Intent to ship axis.tooltip
Browse files Browse the repository at this point in the history
Implement realtime coordinate axis tooltip

Close #3603
  • Loading branch information
netil authored Apr 8, 2024
1 parent 0dbb63b commit 54e77cb
Show file tree
Hide file tree
Showing 20 changed files with 365 additions and 29 deletions.
52 changes: 52 additions & 0 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,58 @@ var demos = {
}
}
},
AxisTooltip: [
{
options: {
data: {
columns: [
["data1", 300, 350, 300, 120, 220, 250],
["data2", 130, 100, 140, 200, 150, 50]
],
type: "line",
axes: {
data1: "y",
data2: "y2"
}
},
axis: {
tooltip: {
backgroundColor: {
x: "red",
y: "blue",
y2: "green"
}
},
y2: {
show: true
}
}
}
},
{
options: {
data: {
columns: [
["data1", 300, 350, 300, 120, 220, 250],
["data2", 130, 100, 140, 200, 150, 50]
],
type: "line"
},
axis: {
rotated: true,
tooltip: {
backgroundColor: {
x: "red",
y: "blue"
}
},
y2: {
show: true
}
}
}
}
],
CategoryAxis: {
options: {
data: {
Expand Down
44 changes: 44 additions & 0 deletions src/ChartInternal/Axis/Axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ class Axis {

this.generateAxes(v);
});

config.axis_tooltip && this.setAxisTooltip();
}

/**
Expand Down Expand Up @@ -1043,4 +1045,46 @@ class Axis {
}
});
}

/**
* Set axis tooltip
* @private
*/
setAxisTooltip(): void {
const $$ = this.owner;
const {config: {axis_rotated: isRotated, axis_tooltip}, $el: {axis, axisTooltip}} = $$;
const bgColor = axis_tooltip.backgroundColor ?? "black";

$$.generateTextBGColorFilter(
bgColor,
{
x: -0.15,
y: -0.2,
width: 1.3,
height: 1.3
}
);

["x", "y", "y2"].forEach(v => {
axisTooltip[v] = axis[v]?.append("text")
.classed($AXIS[`axis${v.toUpperCase()}Tooltip`], true)
.attr("filter", $$.updateTextBGColor({id: v}, bgColor));

if (isRotated) {
const pos = v === "x" ? "x" : "y";
const val = v === "y" ? "1.15em" : (v === "x" ? "-0.3em" : "-0.4em");

axisTooltip[v]?.attr(pos, val)
.attr(`d${v === "x" ? "y" : "x"}`, v === "x" ? "0.4em" : "-1.3em")
.style("text-anchor", v === "x" ? "end" : null);
} else {
const pos = v === "x" ? "y" : "x";
const val = v === "x" ? "1.15em" : `${v === "y" ? "-" : ""}0.4em`;

axisTooltip[v]?.attr(pos, val)
.attr(`d${v === "x" ? "x" : "y"}`, v === "x" ? "-1em" : "0.3em")
.style("text-anchor", v === "y" ? "end" : null);
}
});
}
}
5 changes: 3 additions & 2 deletions src/ChartInternal/ChartInternal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ export default class ChartInternal {
const {hasAxis, hasTreemap} = state;
const hasInteraction = config.interaction_enabled;
const hasPolar = $$.hasType("polar");
const labelsBGColor = config.data_labels_backgroundColors;

// for arc type, set axes to not be shown
// $$.hasArcType() && ["x", "y", "y2"].forEach(id => (config[`axis_${id}_show`] = false));
Expand Down Expand Up @@ -430,7 +431,7 @@ export default class ChartInternal {

if (
hasAxis || hasColorPatterns || hasPolar || hasTreemap ||
config.data_labels_backgroundColors || $$.hasLegendDefsPoint?.()
labelsBGColor || $$.hasLegendDefsPoint?.()
) {
$el.defs = $el.svg.append("defs");

Expand All @@ -441,7 +442,7 @@ export default class ChartInternal {
}

// Append data background color filter definition
$$.generateDataLabelBackgroundColorFilter();
$$.generateTextBGColorFilter(labelsBGColor);

// set color patterns
if (hasColorPatterns) {
Expand Down
8 changes: 8 additions & 0 deletions src/ChartInternal/interactions/eventrect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
import type {d3Selection} from "../../../types";
import {$COMMON, $EVENT, $SHAPE} from "../../config/classes";
import {getPointer, getScrollPosition, isboolean, isFunction} from "../../module/util";

Expand Down Expand Up @@ -510,6 +511,9 @@ export default {
.on("mouseover", event => {
state.event = event;
$$.updateEventRect();

Object.values($$.$el.axisTooltip)
.forEach((v: d3Selection) => v?.style("display", null));
})
.on("mousemove", function(event) {
const d = getData(event);
Expand Down Expand Up @@ -539,6 +543,8 @@ export default {
}
}

$$.showAxisGridFocus();

const eventOnSameIdx = config.tooltip_grouped &&
index === eventReceiver.currentIdx;

Expand Down Expand Up @@ -567,6 +573,8 @@ export default {
return;
}

$$.hideAxisGridFocus();

$$.unselectRect();
$$.setOverOut(false, eventReceiver.currentIdx);

Expand Down
36 changes: 20 additions & 16 deletions src/ChartInternal/internals/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,38 +178,42 @@ export default {

/**
* Append data backgound color filter definition
* @param {string} color Color string
* @param {string|object} color Color string
* @param {object} attr filter attribute
* @private
*/
generateDataLabelBackgroundColorFilter(color?: string): void {
generateTextBGColorFilter(color: string | object, attr = {
x: 0,
y: 0,
width: 1,
height: 1
}): void {
const $$ = this;
const {$el, config, state} = $$;
const backgroundColors = color || config.data_labels_backgroundColors;
const {$el, state} = $$;

if (backgroundColors) {
if (color) {
let ids: string[] = [];

if (isString(backgroundColors)) {
if (isString(color)) {
ids.push("");
} else if (isObject(backgroundColors)) {
ids = Object.keys(backgroundColors);
} else if (isObject(color)) {
ids = Object.keys(color);
}

ids.forEach(v => {
const id = `${state.datetimeId}-labels-bg${$$.getTargetSelectorSuffix(v)}${
color ? $$.getTargetSelectorSuffix(color) : ""
isString(color) ? $$.getTargetSelectorSuffix(color) : ""
}`;

$el.defs.append("filter")
.attr("x", "0")
.attr("y", "0")
.attr("width", "1")
.attr("height", "1")
.attr("x", attr.x)
.attr("y", attr.y)
.attr("width", attr.width)
.attr("height", attr.height)
.attr("id", id)
.html(
`<feFlood flood-color="${
v === "" ? backgroundColors : backgroundColors[v]
}" /><feComposite in="SourceGraphic"/>`
`<feFlood flood-color="${v === "" ? color : color[v]}" />
<feComposite in="SourceGraphic" />`
);
});
}
Expand Down
70 changes: 68 additions & 2 deletions src/ChartInternal/internals/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* billboard.js project is licensed under the MIT license
*/
import {select as d3Select, selectAll as d3SelectAll} from "d3-selection";
import type {d3Selection} from "../../../types/types";
import {$AXIS, $COMMON, $FOCUS, $GRID} from "../../config/classes";
import {isArray, isValue} from "../../module/util";
import {getPointer, isArray, isValue} from "../../module/util";

// Grid position and text anchor helpers
const getGridTextAnchor = d => isValue(d.position) || "end";
Expand Down Expand Up @@ -296,7 +297,14 @@ export default {
config.grid_y_show &&
grid.append("g").attr("class", $GRID.ygrids);

if (config.interaction_enabled && config.grid_focus_show) {
if (config.axis_tooltip) {
const axis = grid.append("g").attr("class", "bb-axis-tooltip");

axis.append("line").attr("class", "bb-axis-tooltip-x");
axis.append("line").attr("class", "bb-axis-tooltip-y");
}

if (config.interaction_enabled && config.grid_focus_show && !config.axis_tooltip) {
grid.append("g")
.attr("class", $FOCUS.xgridFocus)
.append("line")
Expand All @@ -312,6 +320,64 @@ export default {
}
},

showAxisGridFocus() {
const $$ = this;
const {config, format, state: {event, width, height}} = $$;
const isRotated = config.axis_rotated;

// get mouse event position
const [x, y] = getPointer(event, $$.$el.eventRect?.node());
const pos = {x, y};

for (const [axis, node] of Object.entries($$.$el.axisTooltip)) {
const attr = (axis === "x" && !isRotated) || (axis !== "x" && isRotated) ? "x" : "y";
const value = pos[attr];
let scaleText = $$.scale[axis]?.invert(value);

if (scaleText) {
scaleText = axis === "x" && $$.axis.isTimeSeries() ?
format.xAxisTick(scaleText) :
scaleText?.toFixed(2);

// set position & its text value based on position
(node as d3Selection)?.attr(attr, value)
.text(scaleText);
}
}

$$.$el.main.selectAll(
`line.bb-axis-tooltip-x, line.bb-axis-tooltip-y`
).style("visibility", null)
.each(function(d, i) {
const line = d3Select(this);

if (i === 0) {
line
.attr("x1", x)
.attr("x2", x)
.attr("y1", i ? 0 : height)
.attr("y2", i ? height : 0);
} else {
line
.attr("x1", i ? 0 : width)
.attr("x2", i ? width : 0)
.attr("y1", y)
.attr("y2", y);
}
});
},

hideAxisGridFocus() {
const $$ = this;

$$.$el.main.selectAll(
`line.bb-axis-tooltip-x, line.bb-axis-tooltip-y`
).style("visibility", "hidden");

Object.values($$.$el.axisTooltip)
.forEach((v: d3Selection) => v?.style("display", "none"));
},

/**
* Show grid focus line
* @param {Array} data Selected data
Expand Down
13 changes: 7 additions & 6 deletions src/ChartInternal/internals/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,17 +271,17 @@ export default {
/**
* Update data label text background color
* @param {object} d Data object
* @param {object|string} option option object
* @returns {string|null}
* @private
*/
updateTextBackgroundColor(d: IDataRow | IArcData): string | null {
updateTextBGColor(d: IDataRow | IArcData, option): string | null {
const $$ = this;
const {$el, config} = $$;
const backgroundColor = config.data_labels_backgroundColors;
const {$el} = $$;
let color: string = "";

if (isString(backgroundColor) || isObject(backgroundColor)) {
const id = isString(backgroundColor) ?
if (isString(option) || isObject(option)) {
const id = isString(option) ?
"" :
$$.getTargetSelectorSuffix("id" in d ? d.id : d.data.id);
const filter = $el.defs.select(["filter[id*='labels-bg", "']"].join(id));
Expand Down Expand Up @@ -314,7 +314,8 @@ export default {

$$.$el.text
.style("fill", $$.getStylePropValue($$.updateTextColor))
.attr("filter", $$.updateTextBackgroundColor.bind($$))
.attr("filter",
d => $$.updateTextBGColor.bind($$)(d, config.data_labels_backgroundColors))
.style("fill-opacity", forFlow ? 0 : $$.opacityForText.bind($$))
.each(function(d: IDataRow, i: number) {
// do not apply transition for newly added text elements
Expand Down
3 changes: 2 additions & 1 deletion src/ChartInternal/shape/arc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,8 @@ export default {
if ($$.shouldShowArcLabel()) {
selection
.style("fill", $$.updateTextColor.bind($$))
.attr("filter", $$.updateTextBackgroundColor.bind($$))
.attr("filter", d =>
$$.updateTextBGColor.bind($$)(d, $$.config.data_labels_backgroundColors))
.each(function(d) {
const node = d3Select(this);
const updated = $$.updateAngle(d);
Expand Down
2 changes: 1 addition & 1 deletion src/ChartInternal/shape/polar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default {

// set level text background color
if (levelTextShow && levelTextBgColor) {
$$.generateDataLabelBackgroundColorFilter(levelTextBgColor);
$$.generateTextBGColorFilter(levelTextBgColor);
}
},

Expand Down
Loading

0 comments on commit 54e77cb

Please sign in to comment.