Skip to content

Commit

Permalink
Feature: allow to drag over chart (#173)
Browse files Browse the repository at this point in the history
* feat: allowed drag with tooltip over the chart

* fix: fixed unit tests

* fix: fixde setCursorLeaved method

* fix: fix onMouseLeave

* fix: fixed drag and mouseleave

* fix: fix setCursor on mouse leave

* fix: fixed issues in PR
  • Loading branch information
zefirka authored Oct 23, 2023
1 parent 05f9964 commit 709478f
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 30 deletions.
32 changes: 16 additions & 16 deletions demo/examples/tooltip-with-datarefs.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ <h1>Tooltip Plugin</h1>
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},
Expand Down Expand Up @@ -112,23 +112,23 @@ <h1>Tooltip Plugin</h1>
return `
<tr>
<td>${id}</td>
<td>${ref.min.toFixed(2) ?? '-'}</td>
<td>${ref.max.toFixed(2) ?? '-'}</td>
<td>${ref.avg.toFixed(2) ?? '-'}</td>
<td>${ref.sum.toFixed(2) ?? '-'}</td>
<td>${ref.count.toFixed(2) ?? '-'}</td>
<td>${ref.integral.toFixed(2) ?? '-'}</td>
<td>${ref.last ?? '-'}</td>
<td>${ref.min.toFixed(1) ?? '-'}</td>
<td>${ref.max.toFixed(1) ?? '-'}</td>
<td>${ref.avg.toFixed(1) ?? '-'}</td>
<td>${ref.sum.toFixed(1) ?? '-'}</td>
<td>${ref.count.toFixed(1) ?? '-'}</td>
<td>${ref.integral.toFixed(1) ?? '-'}</td>
<td>${ref.last?.toFixed(1) ?? '-'}</td>
</tr>`;
})}
<tr>
<td>Total</td>
<td>${refs.y.total.min.toFixed(2)}</td>
<td>${refs.y.total.max.toFixed(2)}</td>
<td>${refs.y.total.avg.toFixed(2)}</td>
<td>${refs.y.total.sum.toFixed(2)}</td>
<td>${refs.y.total.count.toFixed(2)}</td>
<td>${refs.y.total.integral.toFixed(2)}</td>
<td>${refs.y.total.min.toFixed(1)}</td>
<td>${refs.y.total.max.toFixed(1)}</td>
<td>${refs.y.total.avg.toFixed(1)}</td>
<td>${refs.y.total.sum.toFixed(1)}</td>
<td>${refs.y.total.count.toFixed(1)}</td>
<td>${refs.y.total.integral.toFixed(1)}</td>
<td>${refs.y.total.last ?? '-'}</td>
</tbody>
</table>`;
Expand Down
82 changes: 74 additions & 8 deletions src/YagrCore/plugins/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class YagrTooltip {

private bLeft: number;
private bTop: number;
private bWidth: number;

constructor(yagr: Yagr, options: Partial<TooltipOptions> = {}) {
this.yagr = yagr;
Expand Down Expand Up @@ -153,6 +154,7 @@ class YagrTooltip {

this.bLeft = 0;
this.bTop = 0;
this.bWidth = 0;

if (this.opts.virtual) {
this.placement = () => {};
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -435,14 +437,15 @@ class YagrTooltip {
if (hasOneRow) {
this.onMouseEnter();
} else {
this.onMouseLeave();
this.hide();
return;
}

const bbox = this.over.getBoundingClientRect();

this.bLeft = bbox.left;
this.bTop = bbox.top;
this.bWidth = bbox.width;

const anchor = {
left: left + this.bLeft,
Expand Down Expand Up @@ -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 = () => {
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
};
Expand Down Expand Up @@ -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];
}
}

/*
Expand Down
32 changes: 26 additions & 6 deletions tests/plugins/tooltip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ describe('tooltip', () => {
});

expect(window.document.querySelector(`#${yagr.id}_tooltip`)).toBeTruthy();

yagr.dispose();
});

it('should render tooltip sum', async () => {
Expand All @@ -42,6 +44,8 @@ describe('tooltip', () => {
yagr.plugins.tooltip?.on('show', () => {
expect(window.document.querySelector(`.__section_sum`)).not.toBeNull();
});

yagr.dispose();
});
});

Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -110,6 +119,11 @@ describe('tooltip', () => {

expect(handler).toBeCalledTimes(1);
});

afterAll(() => {
yagr.dispose();
window.document.body.innerHTML = '';
});
});

describe('pinning', () => {
Expand All @@ -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);
});

Expand All @@ -139,22 +153,28 @@ 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: {
strategy: 'pin',
},
});

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);
});

Expand All @@ -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();
});
Expand All @@ -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();
});
Expand Down

0 comments on commit 709478f

Please sign in to comment.