diff --git a/demo/examples/tooltip-with-datarefs.html b/demo/examples/tooltip-with-datarefs.html
index e300a6f1..e8d933df 100644
--- a/demo/examples/tooltip-with-datarefs.html
+++ b/demo/examples/tooltip-with-datarefs.html
@@ -42,10 +42,10 @@
Tooltip Plugin
title: {
text: 'By scale, constant',
},
- timeline: new Array(5).fill().map((_, i) => i * 1000),
+ timeline: new Array(100).fill().map((_, i) => i * 1000),
series: [
- {data: new Array(5).fill().map((_, i) => Math.random() * 6), color: 'red'},
- {data: new Array(5).fill().map((_, i) => Math.random() * 6), color: 'green'},
+ {data: new Array(100).fill().map((_, i) => Math.random() * 6), color: 'red'},
+ {data: new Array(100).fill().map((_, i) => Math.random() * 6), color: 'green'},
],
chart: {
select: {zoom: false},
@@ -112,23 +112,23 @@ Tooltip Plugin
return `
${id} |
- ${ref.min.toFixed(2) ?? '-'} |
- ${ref.max.toFixed(2) ?? '-'} |
- ${ref.avg.toFixed(2) ?? '-'} |
- ${ref.sum.toFixed(2) ?? '-'} |
- ${ref.count.toFixed(2) ?? '-'} |
- ${ref.integral.toFixed(2) ?? '-'} |
- ${ref.last ?? '-'} |
+ ${ref.min.toFixed(1) ?? '-'} |
+ ${ref.max.toFixed(1) ?? '-'} |
+ ${ref.avg.toFixed(1) ?? '-'} |
+ ${ref.sum.toFixed(1) ?? '-'} |
+ ${ref.count.toFixed(1) ?? '-'} |
+ ${ref.integral.toFixed(1) ?? '-'} |
+ ${ref.last?.toFixed(1) ?? '-'} |
`;
})}
Total |
- ${refs.y.total.min.toFixed(2)} |
- ${refs.y.total.max.toFixed(2)} |
- ${refs.y.total.avg.toFixed(2)} |
- ${refs.y.total.sum.toFixed(2)} |
- ${refs.y.total.count.toFixed(2)} |
- ${refs.y.total.integral.toFixed(2)} |
+ ${refs.y.total.min.toFixed(1)} |
+ ${refs.y.total.max.toFixed(1)} |
+ ${refs.y.total.avg.toFixed(1)} |
+ ${refs.y.total.sum.toFixed(1)} |
+ ${refs.y.total.count.toFixed(1)} |
+ ${refs.y.total.integral.toFixed(1)} |
${refs.y.total.last ?? '-'} |
`;
diff --git a/src/YagrCore/plugins/tooltip/tooltip.ts b/src/YagrCore/plugins/tooltip/tooltip.ts
index cc67920a..7b77b352 100644
--- a/src/YagrCore/plugins/tooltip/tooltip.ts
+++ b/src/YagrCore/plugins/tooltip/tooltip.ts
@@ -119,6 +119,7 @@ class YagrTooltip {
private bLeft: number;
private bTop: number;
+ private bWidth: number;
constructor(yagr: Yagr, options: Partial = {}) {
this.yagr = yagr;
@@ -153,6 +154,7 @@ class YagrTooltip {
this.bLeft = 0;
this.bTop = 0;
+ this.bWidth = 0;
if (this.opts.virtual) {
this.placement = () => {};
@@ -295,7 +297,7 @@ class YagrTooltip {
return;
}
- if ((left < 0 || top < 0) && !state.pinned) {
+ if ((left < 0 || top < 0) && !state.pinned && this.isNotInDrag) {
this.hide();
}
@@ -435,7 +437,7 @@ class YagrTooltip {
if (hasOneRow) {
this.onMouseEnter();
} else {
- this.onMouseLeave();
+ this.hide();
return;
}
@@ -443,6 +445,7 @@ class YagrTooltip {
this.bLeft = bbox.left;
this.bTop = bbox.top;
+ this.bWidth = bbox.width;
const anchor = {
left: left + this.bLeft,
@@ -491,10 +494,11 @@ class YagrTooltip {
this.over = u.root.querySelector('.u-over') as HTMLDivElement;
this.over.addEventListener('mousedown', this.onMouseDown);
- this.over.addEventListener('mouseup', this.onMouseUp);
this.over.addEventListener('mousemove', this.onMouseMove);
this.over.addEventListener('mouseenter', this.onMouseEnter);
this.over.addEventListener('mouseleave', this.onMouseLeave);
+
+ document.addEventListener('mouseup', this.onMouseUp);
};
setSize = () => {
@@ -507,11 +511,11 @@ class YagrTooltip {
dispose = () => {
/** Free overlay listeners */
this.over.removeEventListener('mousedown', this.onMouseDown);
- this.over.removeEventListener('mouseup', this.onMouseUp);
this.over.removeEventListener('mousemove', this.onMouseMove);
this.over.removeEventListener('mouseenter', this.onMouseEnter);
this.over.removeEventListener('mouseleave', this.onMouseLeave);
+ document.removeEventListener('mouseup', this.onMouseUp);
document.removeEventListener('mousemove', this.checkFocus);
document.removeEventListener('mousedown', this.detectClickOutside);
@@ -569,9 +573,58 @@ class YagrTooltip {
}
};
- private onMouseUp = () => {
+ /**
+ * Calculates where exactly cursor leaved the chart
+ * and sets range[1] to this position
+ */
+ private setCursorLeaved = (e: MouseEvent) => {
+ const rect = this.over.getBoundingClientRect();
+ const x = e.clientX;
+ const range = this.state.range!;
+ const startPoint = range[0]!;
+ const xInOver = x - rect.left;
+ const end = xInOver > startPoint.clientX;
+ const timeline = this.yagr.config.timeline;
+
+ let result;
+ if (end) {
+ range[1] = {
+ clientX: this.bWidth,
+ value: this.yagr.uplot.posToVal(this.bWidth, 'x'),
+ idx: timeline.length - 1,
+ };
+ result = range[1];
+ } else {
+ /** Swap range[1] and range[0] in case if tooltip leaved chart in begining of element */
+ range[1] = range[0];
+ range[0] = {
+ clientX: 0,
+ value: this.yagr.uplot.posToVal(0, 'x'),
+ idx: 0,
+ };
+
+ result = range[0];
+ }
+
+ this.yagr.uplot.setCursor({
+ left: result.clientX,
+ top: e.clientY - rect.top,
+ });
+ };
+
+ private onMouseUp = (e: MouseEvent) => {
+ if (this.state.range === null) {
+ return;
+ }
+
const [from] = this.state.range || [];
- const cursor = this.getCursorPosition();
+ let cursor: SelectionRange[number];
+
+ if (e.target === this.over) {
+ cursor = this.getCursorPosition();
+ } else {
+ cursor = this.state.range[1];
+ }
if (this.opts.strategy === 'none') {
return;
@@ -599,8 +652,14 @@ class YagrTooltip {
this.show();
};
- private onMouseLeave = () => {
- if (!this.state.pinned) {
+ private onMouseLeave = (e: MouseEvent) => {
+ const isPinned = this.state.pinned;
+
+ if (this.state.range?.[0]) {
+ this.setCursorLeaved(e);
+ }
+
+ if (!isPinned && this.isNotInDrag) {
this.hide();
}
};
@@ -650,6 +709,13 @@ class YagrTooltip {
get stripValue() {
return this.interpolation ? this.interpolation.value : undefined;
}
+ get isNotInDrag() {
+ if (this.opts.strategy === 'none' || this.opts.strategy === 'pin') {
+ return true;
+ }
+
+ return !this.state.range?.[1];
+ }
}
/*
diff --git a/tests/plugins/tooltip.test.ts b/tests/plugins/tooltip.test.ts
index 501eeeb3..c5ec192f 100644
--- a/tests/plugins/tooltip.test.ts
+++ b/tests/plugins/tooltip.test.ts
@@ -21,6 +21,8 @@ describe('tooltip', () => {
});
expect(window.document.querySelector(`#${yagr.id}_tooltip`)).toBeTruthy();
+
+ yagr.dispose();
});
it('should render tooltip sum', async () => {
@@ -42,6 +44,8 @@ describe('tooltip', () => {
yagr.plugins.tooltip?.on('show', () => {
expect(window.document.querySelector(`.__section_sum`)).not.toBeNull();
});
+
+ yagr.dispose();
});
});
@@ -79,6 +83,11 @@ describe('tooltip', () => {
it('should render tooltip sum', () => {
expect(tElem.querySelectorAll('.__section_sum').length).toBe(1);
});
+
+ afterAll(() => {
+ y.dispose();
+ y.root.remove();
+ });
});
describe('on/off', () => {
@@ -110,6 +119,11 @@ describe('tooltip', () => {
expect(handler).toBeCalledTimes(1);
});
+
+ afterAll(() => {
+ yagr.dispose();
+ window.document.body.innerHTML = '';
+ });
});
describe('pinning', () => {
@@ -124,7 +138,7 @@ describe('tooltip', () => {
yagr.uplot.over.dispatchEvent(new MouseEvent('mousedown', {clientX: 30, clientY: 30}));
await new Promise((resolve) => setTimeout(resolve, 100));
- yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 30, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 30, clientY: 30, bubbles: true}));
expect(yagr.plugins.tooltip?.state.pinned).toBe(false);
});
@@ -139,12 +153,18 @@ describe('tooltip', () => {
yagr.uplot.over.dispatchEvent(new MouseEvent('mousedown', {clientX: 30, clientY: 30}));
await new Promise((resolve) => setTimeout(resolve, 100));
- yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 30, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 30, clientY: 30, bubbles: true}));
expect(yagr.plugins.tooltip?.state.pinned).toBe(false);
});
it('should pin tooltip if strategy=pin', async () => {
const yagr = gen({
+ chart: {
+ size: {
+ width: 600,
+ height: 400,
+ },
+ },
timeline: [1, 2, 3, 4],
series: [{data: [1, 2, 3, 4]}],
tooltip: {
@@ -152,9 +172,9 @@ describe('tooltip', () => {
},
});
- yagr.uplot.over.dispatchEvent(new MouseEvent('mousedown', {clientX: 30, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mousedown', {clientX: 100, clientY: 30}));
await new Promise((resolve) => setTimeout(resolve, 100));
- yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 30, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 100, clientY: 30, bubbles: true}));
expect(yagr.plugins.tooltip?.state.pinned).toBe(true);
});
@@ -172,7 +192,7 @@ describe('tooltip', () => {
yagr.uplot.over.dispatchEvent(new MouseEvent('mousemove', {clientX: 40, clientY: 30}));
await new Promise((resolve) => setTimeout(resolve, 100));
expect(yagr.plugins.tooltip?.state.range).toHaveLength(2);
- yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 40, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 40, clientY: 30, bubbles: true}));
expect(yagr.plugins.tooltip?.state.pinned).toBe(false);
expect(yagr.plugins.tooltip?.state.range).toBeNull();
});
@@ -191,7 +211,7 @@ describe('tooltip', () => {
yagr.uplot.over.dispatchEvent(new MouseEvent('mousemove', {clientX: 40, clientY: 30}));
await new Promise((resolve) => setTimeout(resolve, 100));
expect(yagr.plugins.tooltip?.state.range).toHaveLength(2);
- yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 40, clientY: 30}));
+ yagr.uplot.over.dispatchEvent(new MouseEvent('mouseup', {clientX: 40, clientY: 30, bubbles: true}));
expect(yagr.plugins.tooltip?.state.pinned).toBe(true);
expect(yagr.plugins.tooltip?.state.range).toBeNull();
});