Skip to content

Commit

Permalink
fix(load): Enhance tooltip update on dynamic load
Browse files Browse the repository at this point in the history
- Enhance tooltip update dynamically on redraws
- refactoring on handling events
- Improve .tooltip.show() param to accept 'id' for Arc types

Fix #3305
  • Loading branch information
netil committed Aug 4, 2023
1 parent f2fa81d commit 1df1bd9
Show file tree
Hide file tree
Showing 13 changed files with 566 additions and 106 deletions.
6 changes: 4 additions & 2 deletions src/Chart/api/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* billboard.js project is licensed under the MIT license
*/
import {requestIdleCallback} from "../../module/browser";
import {isString, isArray} from "../../module/util";
import {isString, isArray, isEmpty} from "../../module/util";
import {callDone} from "../../ChartInternal/data/load";

export default {
Expand Down Expand Up @@ -182,7 +182,6 @@ export default {
requestIdleCallback(() => $$.loadFromArgs(args));
});
} else {
// $$.api.tooltip.hide();
$$.loadFromArgs(args);
}
},
Expand Down Expand Up @@ -216,6 +215,9 @@ export default {
const $$ = this.internal;
let args = argsValue || {};

// hide possible tooltip display when data is completely unloaded
isEmpty(args) && this.tooltip.hide();

if (isArray(args)) {
args = {ids: args};
} else if (isString(args)) {
Expand Down
14 changes: 11 additions & 3 deletions src/Chart/api/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
import {isValue, isDefined} from "../../module/util";
import {isDefined} from "../../module/util";

/**
* Define tooltip
Expand Down Expand Up @@ -48,6 +48,10 @@ const tooltip = {
* }
* });
*
* // for Arc types, specify 'id' or 'index'
* chart.tooltip.show({ data: { id: "data2" }});
* chart.tooltip.show({ data: { index: 2 }});
*
* // when data.xs is used
* chart.tooltip.show({
* data: {
Expand Down Expand Up @@ -80,7 +84,7 @@ const tooltip = {
// determine focus data
if (args.data) {
const {data} = args;
const y = $$.getYScaleById(data.id)(data.value);
const y = $$.getYScaleById(data.id)?.(data.value);

if (hasTreemap && data.id) {
eventReceiver.rect = $el.main.select(`${$$.selectorTarget(data.id, undefined, "rect")}`);
Expand All @@ -92,7 +96,11 @@ const tooltip = {
mouse = [0, y];
}

index = isValue(data.index) ? data.index : $$.getIndexByX(data.x);
index = data.index ?? (
$$.hasArcType() && data.id ?
$$.getArcElementByIdOrIndex(data.id)?.datum().index :
$$.getIndexByX(data.x)
);
}
} else if (isDefined(args.x)) {
index = $$.getIndexByX(args.x);
Expand Down
41 changes: 29 additions & 12 deletions src/ChartInternal/data/data.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 {select as d3Select} from "d3-selection";
import {$BAR, $CANDLESTICK, $COMMON} from "../../config/classes";
import {KEY} from "../../module/Cache";
import type {IData, IDataPoint, IDataRow} from "./IData";
Expand Down Expand Up @@ -664,18 +665,34 @@ export default {
*/
getDataIndexFromEvent(event): number {
const $$ = this;
const {config, state: {inputType, eventReceiver: {coords, rect}}} = $$;
const isRotated = config.axis_rotated;

// get data based on the mouse coords
const e = inputType === "touch" && event.changedTouches ? event.changedTouches[0] : event;
const index = findIndex(
coords,
isRotated ? e.clientY - rect.top : e.clientX - rect.left,
0,
coords.length - 1,
isRotated
);
const {config, state: {hasRadar, inputType, eventReceiver: {coords, rect}}} = $$;
let index;

if (hasRadar) {
let target = event.target;

// in case of multilined axis text
if (/tspan/i.test(target.tagName)) {
target = target.parentNode;
}

const d: any = d3Select(target).datum();

index = d && Object.keys(d).length === 1 ? d.index : undefined;
} else {
const isRotated = config.axis_rotated;

// get data based on the mouse coords
const e = inputType === "touch" && event.changedTouches ? event.changedTouches[0] : event;

index = findIndex(
coords,
isRotated ? e.clientY - rect.top : e.clientX - rect.left,
0,
coords.length - 1,
isRotated
);
}

return index;
},
Expand Down
83 changes: 75 additions & 8 deletions src/ChartInternal/interactions/eventrect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export default {
$el.eventRect = eventRectUpdate;

if ($$.state.inputType === "touch" && !$el.svg.on("touchstart.eventRect") && !$$.hasArcType()) {
$$.bindTouchOnEventRect(isMultipleX);
$$.bindTouchOnEventRect();
}

// when initilazed with empty data and data loaded later, need to update eventRect
Expand All @@ -87,12 +87,12 @@ export default {
$$.updateEventRectData();
},

bindTouchOnEventRect(isMultipleX: boolean): void {
bindTouchOnEventRect(): void {
const $$ = this;
const {config, state, $el: {eventRect, svg}} = $$;

const selectRect = context => {
if (isMultipleX) {
if ($$.isMultipleX()) {
$$.selectRectForMultipleXs(context);
} else {
const index = $$.getDataIndexFromEvent(state.event);
Expand All @@ -101,7 +101,7 @@ export default {

index === -1 ?
$$.unselectRect() :
$$.selectRectForSingle(context, eventRect, index);
$$.selectRectForSingle(context, index);
}
};

Expand Down Expand Up @@ -313,7 +313,74 @@ export default {
});
},

selectRectForMultipleXs(context): void {
/**
* Seletct rect for single x value
* @param {d3Selection} context Event rect element
* @param {number} index x Axis index
* @private
*/
selectRectForSingle(context: SVGRectElement, index: number): void {
const $$ = this;
const {config, $el: {main, circle}} = $$;
const isSelectionEnabled = config.data_selection_enabled;
const isSelectionGrouped = config.data_selection_grouped;
const isSelectable = config.data_selection_isselectable;
const isTooltipGrouped = config.tooltip_grouped;
const selectedData = $$.getAllValuesOnIndex(index);

if (isTooltipGrouped) {
$$.showTooltip(selectedData, context);
$$.showGridFocus?.(selectedData);

if (!isSelectionEnabled || isSelectionGrouped) {
return;
}
}

// remove possible previous focused state
!circle && main.selectAll(`.${$COMMON.EXPANDED}:not(.${$SHAPE.shape}-${index})`).classed($COMMON.EXPANDED, false);

const shapeAtIndex = main.selectAll(`.${$SHAPE.shape}-${index}`)
.classed($COMMON.EXPANDED, true)
.style("cursor", isSelectable ? "pointer" : null)
.filter(function(d) {
return $$.isWithinShape(this, d);
});

if (shapeAtIndex.empty() && !isTooltipGrouped) {
$$.hideGridFocus?.();
$$.hideTooltip();

!isSelectionGrouped && $$.setExpand(index);
}

shapeAtIndex
.call(selected => {
const d = selected.data();

if (isSelectionEnabled &&
(isSelectionGrouped || isSelectable?.bind($$.api)(d))
) {
context.style.cursor = "pointer";
}

if (!isTooltipGrouped) {
$$.showTooltip(d, context);
$$.showGridFocus?.(d);
$$.unexpandCircles?.();

selected.each(d => $$.setExpand(index, d.id));
}
});
},

/**
* Select rect for multiple x values
* @param {d3Selection} context Event rect element
* @param {boolean} [triggerEvent=true] Whether trigger event or not
* @private
*/
selectRectForMultipleXs(context: SVGRectElement, triggerEvent = true): void {
const $$ = this;
const {config, state} = $$;
const targetsToShow = $$.filterTargetsToShow($$.data.targets);
Expand All @@ -326,7 +393,7 @@ export default {
const mouse = getPointer(state.event, context);
const closest = $$.findClosestFromTargets(targetsToShow, mouse);

if (state.mouseover && (!closest || closest.id !== state.mouseover.id)) {
if (triggerEvent && state.mouseover && (!closest || closest.id !== state.mouseover.id)) {
config.data_onout.call($$.api, state.mouseover);
state.mouseover = undefined;
}
Expand Down Expand Up @@ -357,7 +424,7 @@ export default {
if ($$.isBarType(closest.id) || dist < $$.getPointSensitivity(closest)) {
$$.$el.svg.select(`.${$EVENT.eventRect}`).style("cursor", "pointer");

if (!state.mouseover) {
if (triggerEvent && !state.mouseover) {
config.data_onover.call($$.api, closest);
state.mouseover = closest;
}
Expand Down Expand Up @@ -461,7 +528,7 @@ export default {
}

index === -1 ?
$$.unselectRect() : $$.selectRectForSingle(this, rect, index);
$$.unselectRect() : $$.selectRectForSingle(this, index);

// As of individual data point(or <path>) element can't bind mouseover/out event
// to determine current interacting element, so use 'mousemove' event instead.
Expand Down
59 changes: 2 additions & 57 deletions src/ChartInternal/interactions/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,6 @@ import {emulateEvent, getPointer, isNumber, isObject} from "../../module/util";
import type {IArcDataRow} from "../data/IData";

export default {
selectRectForSingle(context, eventRect, index: number): void {
const $$ = this;
const {config, $el: {main, circle}} = $$;
const isSelectionEnabled = config.data_selection_enabled;
const isSelectionGrouped = config.data_selection_grouped;
const isSelectable = config.data_selection_isselectable;
const isTooltipGrouped = config.tooltip_grouped;
const selectedData = $$.getAllValuesOnIndex(index);

if (isTooltipGrouped) {
$$.showTooltip(selectedData, context);
$$.showGridFocus?.(selectedData);

if (!isSelectionEnabled || isSelectionGrouped) {
return;
}
}

// remove possible previous focused state
!circle && main.selectAll(`.${$COMMON.EXPANDED}:not(.${$SHAPE.shape}-${index})`).classed($COMMON.EXPANDED, false);

const shapeAtIndex = main.selectAll(`.${$SHAPE.shape}-${index}`)
.classed($COMMON.EXPANDED, true)
.style("cursor", isSelectable ? "pointer" : null)
.filter(function(d) {
return $$.isWithinShape(this, d);
});

if (shapeAtIndex.empty() && !isTooltipGrouped) {
$$.hideGridFocus?.();
$$.hideTooltip();

!isSelectionGrouped && $$.setExpand(index);
}

shapeAtIndex
.call(selected => {
const d = selected.data();

if (isSelectionEnabled &&
(isSelectionGrouped || isSelectable?.bind($$.api)(d))
) {
eventRect.style("cursor", "pointer");
}

if (!isTooltipGrouped) {
$$.showTooltip(d, context);
$$.showGridFocus?.(d);
$$.unexpandCircles?.();

selected.each(d => $$.setExpand(index, d.id));
}
});
},

/**
* Expand data shape/point
* @param {number} index Index number
Expand Down Expand Up @@ -225,11 +170,11 @@ export default {
const $$ = this;
const {config, state: {
eventReceiver, hasAxis, hasRadar, hasTreemap
}, $el: {eventRect, arcs, radar, treemap}} = $$;
}, $el: {eventRect, radar, treemap}} = $$;
const element = (
(hasTreemap && eventReceiver.rect) ||
(hasRadar && radar.axes.select(`.${$AXIS.axis}-${index} text`)) || (
eventRect || arcs?.selectAll(`.${$COMMON.target} path`).filter((d, i) => i === index)
eventRect || $$.getArcElementByIdOrIndex?.(index)
)
)?.node();

Expand Down
2 changes: 2 additions & 0 deletions src/ChartInternal/internals/redraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export default {
initializing && $$.updateTypesElements();

$$.generateRedrawList(targetsToShow, flow, duration, wth.Subchart);
$$.updateTooltipOnRedraw();

$$.callPluginHook("$redraw", options, duration);
},

Expand Down

0 comments on commit 1df1bd9

Please sign in to comment.