Skip to content

Commit 6b23952

Browse files
fix: replace deprecated .interactive with eventMode
1 parent 5db7575 commit 6b23952

6 files changed

Lines changed: 109 additions & 55 deletions

File tree

timeline-chart/src/components/time-graph-component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export abstract class TimeGraphComponent<T> {
151151
}
152152

153153
addEvent(event: TimeGraphInteractionType, handler: TimeGraphInteractionHandler, displayObject: PIXI.DisplayObject) {
154-
displayObject.interactive = true;
154+
displayObject.eventMode = 'static';
155155
displayObject.on(event, ((e: PIXI.FederatedPointerEvent) => {
156156
if (handler) {
157157
handler(e);

timeline-chart/src/layer/__tests__/time-graph-chart-rich-cursor.test.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import { TimeGraphChart } from '../time-graph-chart';
77

88
// Mock the chart layer
99
function createMockChart(states: any[] = []) {
10+
const rowComponents = new Map();
1011
return {
1112
getStatesAtTimestamp: jest.fn().mockReturnValue(states),
12-
rowComponents: new Map()
13+
getRowComponent: (id: number) => rowComponents.get(id),
14+
getRowComponents: () => rowComponents,
15+
rowComponents
1316
} as unknown as TimeGraphChart;
1417
}
1518

@@ -50,13 +53,13 @@ describe('TimeGraphChartRichCursor', () => {
5053
it('registers mousemove and mouseleave handlers', () => {
5154
const { cursor } = createSetup();
5255
expect((cursor as any)._mouseMoveHandler).toBeDefined();
53-
expect((cursor as any)._mouseLeaveHandler).toBeDefined();
56+
expect((cursor as any)._pointerLeaveHandler).toBeDefined();
5457
});
5558

56-
it('adds graphics and tooltip container to stage', () => {
57-
const { stage } = createSetup();
58-
// stage has layer + graphics + tooltipContainer
59-
expect(stage.children.length).toBeGreaterThanOrEqual(3);
59+
it('adds graphics and tooltip container to layer', () => {
60+
const { cursor } = createSetup();
61+
// layer has graphics + tooltipContainer
62+
expect((cursor as any).layer.children.length).toBeGreaterThanOrEqual(2);
6063
});
6164
});
6265

@@ -136,7 +139,8 @@ describe('TimeGraphChartRichCursor', () => {
136139
(chartLayer as any).rowComponents.set(1, { position: { y: 10 } });
137140

138141
(cursor as any).drawCursor(100);
139-
expect((cursor as any).tooltipContainer.children.length).toBeGreaterThan(0);
142+
const visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
143+
expect(visibleChildren.length).toBeGreaterThan(0);
140144
});
141145

142146
it('falls back to row name when state has no label', () => {
@@ -148,7 +152,8 @@ describe('TimeGraphChartRichCursor', () => {
148152

149153
(cursor as any).drawCursor(100);
150154
// Should still create tooltip children (bg + text)
151-
expect((cursor as any).tooltipContainer.children.length).toBe(2);
155+
const visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
156+
expect(visibleChildren.length).toBe(2);
152157
});
153158

154159
it('does not create tooltip when no label and no row name', () => {
@@ -159,7 +164,8 @@ describe('TimeGraphChartRichCursor', () => {
159164
(chartLayer as any).rowComponents.set(1, { position: { y: 10 } });
160165

161166
(cursor as any).drawCursor(100);
162-
expect((cursor as any).tooltipContainer.children.length).toBe(0);
167+
const visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
168+
expect(visibleChildren.length).toBe(0);
163169
});
164170

165171
it('flips tooltip to left when near right edge', () => {
@@ -171,9 +177,10 @@ describe('TimeGraphChartRichCursor', () => {
171177

172178
// Draw near right edge (canvas is 800px wide)
173179
(cursor as any).drawCursor(790);
174-
const bg = (cursor as any).tooltipContainer.children[0] as PIXI.Graphics;
175-
// The tooltip box should be positioned to the left of cursor
176-
expect(bg).toBeTruthy();
180+
const textObj = (cursor as any).tooltipContainer.children[1] as PIXI.Text;
181+
// The tooltip text should be positioned to the left of cursor
182+
expect(textObj).toBeTruthy();
183+
expect(textObj.x).toBeLessThan(790);
177184
});
178185

179186
it('clears tooltips on each redraw', () => {
@@ -184,11 +191,13 @@ describe('TimeGraphChartRichCursor', () => {
184191
(chartLayer as any).rowComponents.set(1, { position: { y: 10 } });
185192

186193
(cursor as any).drawCursor(100);
187-
expect((cursor as any).tooltipContainer.children.length).toBe(2);
194+
let visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
195+
expect(visibleChildren.length).toBe(2);
188196

189197
(cursor as any).drawCursor(200);
190-
// Should still be 2 (cleared old, added new)
191-
expect((cursor as any).tooltipContainer.children.length).toBe(2);
198+
// Should still be 2 visible (cleared old, reused for new)
199+
visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
200+
expect(visibleChildren.length).toBe(2);
192201
});
193202

194203
it('creates multiple tooltips for multiple hits', () => {
@@ -204,7 +213,8 @@ describe('TimeGraphChartRichCursor', () => {
204213

205214
(cursor as any).drawCursor(100);
206215
// 3 tooltips × 2 children each (bg + text)
207-
expect((cursor as any).tooltipContainer.children.length).toBe(6);
216+
const visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
217+
expect(visibleChildren.length).toBe(6);
208218
});
209219
});
210220

@@ -217,25 +227,29 @@ describe('TimeGraphChartRichCursor', () => {
217227
(chartLayer as any).rowComponents.set(1, { position: { y: 10 } });
218228

219229
(cursor as any).drawCursor(100);
220-
expect((cursor as any).tooltipContainer.children.length).toBe(2);
230+
let visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
231+
expect(visibleChildren.length).toBe(2);
221232

222233
// Simulate mouseleave
223-
(cursor as any)._mouseLeaveHandler();
224-
expect((cursor as any).tooltipContainer.children.length).toBe(0);
234+
(cursor as any)._pointerLeaveHandler();
235+
visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
236+
expect(visibleChildren.length).toBe(0);
225237
});
226238
});
227239

228240
describe('update', () => {
229-
it('clears graphics and tooltips on update', () => {
241+
it('redraws tooltips on update when mouse is in canvas', () => {
230242
const hits = [
231243
{ row: { id: 1, name: 'R' }, state: { label: 'S', range: { start: BigInt(0), end: BigInt(100) } } }
232244
];
233245
const { cursor, chartLayer } = createSetup(hits);
234246
(chartLayer as any).rowComponents.set(1, { position: { y: 10 } });
235247

236-
(cursor as any).drawCursor(100);
248+
(cursor as any).isMouseInCanvas = true;
249+
(cursor as any).lastMouseX = 100;
237250
cursor.update();
238-
expect((cursor as any).tooltipContainer.children.length).toBe(0);
251+
const visibleChildren = (cursor as any).tooltipContainer.children.filter((c: any) => c.visible);
252+
expect(visibleChildren.length).toBe(2);
239253
});
240254
});
241255

@@ -244,8 +258,8 @@ describe('TimeGraphChartRichCursor', () => {
244258
const { cursor, canvas } = createSetup();
245259
const removeSpy = jest.spyOn(canvas, 'removeEventListener');
246260
cursor.destroy();
247-
expect(removeSpy).toHaveBeenCalledWith('mousemove', expect.any(Function));
248-
expect(removeSpy).toHaveBeenCalledWith('mouseleave', expect.any(Function));
261+
expect(removeSpy).toHaveBeenCalledWith('pointermove', expect.any(Function));
262+
expect(removeSpy).toHaveBeenCalledWith('pointerleave', expect.any(Function));
249263
});
250264
});
251265

timeline-chart/src/layer/time-graph-chart-cursors.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer {
3636
protected afterAddToContainer() {
3737
this.mouseSelecting = false;
3838
this.shiftKeyDown = false
39-
this.stage.interactive = true;
39+
this.stage.eventMode = 'static';
4040

4141
this._updateHandler = (): void => this.update();
4242

@@ -107,8 +107,7 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer {
107107
if ((this.mouseButtons & 1) === 0) {
108108
// handle missed button mouseup event
109109
this.mouseSelecting = false;
110-
const orig = event.nativeEvent as PointerEvent;
111-
if (!orig.shiftKey || orig.ctrlKey || orig.altKey) {
110+
if (!event.shiftKey || event.ctrlKey || event.altKey) {
112111
this.stage.cursor = 'default';
113112
}
114113
return;
@@ -129,8 +128,7 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer {
129128
this.mouseButtons = event.buttons;
130129
if (this.mouseSelecting && event.button === 0) {
131130
this.mouseSelecting = false;
132-
const orig = event.nativeEvent as PointerEvent;
133-
if (!orig.shiftKey || orig.ctrlKey || orig.altKey) {
131+
if (!event.shiftKey || event.ctrlKey || event.altKey) {
134132
this.stage.cursor = 'default';
135133
}
136134
}
@@ -296,7 +294,7 @@ export class TimeGraphChartCursors extends TimeGraphChartLayer {
296294
this.removeOnCanvasEvent('keydown', this._keyboardShortcutKeyDownHandler);
297295
}
298296
if (this._cursorKeyUpHandler) {
299-
this.removeOnCanvasEvent('mousedown', this._cursorKeyUpHandler);
297+
this.removeOnCanvasEvent('keyup', this._cursorKeyUpHandler);
300298
}
301299
if (this.stage) {
302300
this.stage.off('mousedown', this._stageMouseDownHandler);

timeline-chart/src/layer/time-graph-chart-rich-cursor.ts

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ import { TimeGraphRowController } from "../time-graph-row-controller";
55
import { TimeGraphChart } from "./time-graph-chart";
66
import { TimelineChart } from "../time-graph-model";
77
import { BIMath } from "../bigint-utils";
8+
import { RenderEvents } from "../time-graph-render-controller";
89

910
export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
1011
private graphics: PIXI.Graphics;
1112
private tooltipContainer: PIXI.Container;
1213
private _mouseMoveHandler: (event: Event) => void;
14+
private _pointerLeaveHandler: (event: Event) => void;
1315
private lastMouseX: number = -1;
1416
private isMouseInCanvas: boolean = false;
17+
private tooltipPool: { bg: PIXI.Graphics, text: PIXI.Text }[] = [];
18+
private tooltipPoolIndex: number = 0;
1519

1620
constructor(id: string, protected chartLayer: TimeGraphChart, protected rowController: TimeGraphRowController,
1721
private style?: { lineColor?: number, dotColor?: number }) {
@@ -44,11 +48,12 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
4448
};
4549

4650
this.onCanvasEvent('pointermove', this._mouseMoveHandler);
47-
this.onCanvasEvent('pointerleave', () => {
51+
this._pointerLeaveHandler = () => {
4852
this.graphics.clear();
4953
this.removeTooltips();
5054
this.isMouseInCanvas = false;
51-
});
55+
};
56+
this.onCanvasEvent('pointerleave', this._pointerLeaveHandler);
5257
}
5358

5459
private drawCursor(mouseX: number) {
@@ -59,6 +64,7 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
5964

6065
this.graphics.clear();
6166
this.removeTooltips();
67+
RenderEvents.startRender();
6268

6369
// Vertical line
6470
this.graphics.lineStyle(1, lineColor, 0.7);
@@ -72,7 +78,7 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
7278
// Draw a dot and tooltip on each row that has a state
7379
this.graphics.lineStyle(0);
7480
hits.forEach(hit => {
75-
const rowComponent = (this.chartLayer as any).rowComponents.get(hit.row.id);
81+
const rowComponent = this.chartLayer.getRowComponent(hit.row.id);
7682
if (!rowComponent) return;
7783

7884
const y = rowComponent.position.y - this.rowController.verticalOffset;
@@ -91,7 +97,7 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
9197
});
9298

9399
// Draw XY value indicators on rows with xySeries
94-
const rowComponents: Map<number, any> = (this.chartLayer as any).rowComponents;
100+
const rowComponents = this.chartLayer.getRowComponents();
95101
rowComponents.forEach((rowComponent) => {
96102
const model: TimelineChart.TimeGraphRowModel | undefined = rowComponent.model;
97103
if (!model?.xySeries?.length) return;
@@ -143,20 +149,35 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
143149
}
144150

145151
private removeTooltips() {
146-
this.tooltipContainer.removeChildren();
152+
for (let i = 0; i < this.tooltipPool.length; i++) {
153+
this.tooltipPool[i].bg.visible = false;
154+
this.tooltipPool[i].text.visible = false;
155+
}
156+
this.tooltipPoolIndex = 0;
157+
}
158+
159+
private getTooltipPair(): { bg: PIXI.Graphics, text: PIXI.Text } {
160+
if (this.tooltipPoolIndex < this.tooltipPool.length) {
161+
return this.tooltipPool[this.tooltipPoolIndex++];
162+
}
163+
const bg = new PIXI.Graphics();
164+
const text = new PIXI.Text('', { fontSize: 11, fill: 0xffffff, fontFamily: 'Verdana' });
165+
this.tooltipContainer.addChild(bg);
166+
this.tooltipContainer.addChild(text);
167+
const pair = { bg, text };
168+
this.tooltipPool.push(pair);
169+
this.tooltipPoolIndex++;
170+
return pair;
147171
}
148172

149-
private drawTooltip(x: number, y: number, text: string) {
173+
private drawTooltip(x: number, y: number, label: string) {
150174
const padding = 4;
151175
const offsetX = 8;
152-
const fontSize = 11;
153176
const canvasWidth = this.stateController.canvasDisplayWidth;
154177

155-
const textObj = new PIXI.Text(text, {
156-
fontSize,
157-
fill: 0xffffff,
158-
fontFamily: 'Verdana'
159-
});
178+
const { bg, text: textObj } = this.getTooltipPair();
179+
textObj.text = label;
180+
textObj.visible = true;
160181

161182
const boxWidth = textObj.width + padding * 2;
162183
const boxHeight = textObj.height + padding * 2;
@@ -167,17 +188,21 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
167188
boxX = x - offsetX - boxWidth;
168189
}
169190
let boxY = y - boxHeight / 2;
191+
const canvasHeight = this.stateController.canvasDisplayHeight;
192+
if (boxY < 0) {
193+
boxY = 0;
194+
} else if (boxY + boxHeight > canvasHeight) {
195+
boxY = canvasHeight - boxHeight;
196+
}
170197

171-
const bg = new PIXI.Graphics();
198+
bg.clear();
172199
bg.beginFill(0x333333, 0.9);
173200
bg.drawRoundedRect(boxX, boxY, boxWidth, boxHeight, 3);
174201
bg.endFill();
202+
bg.visible = true;
175203

176204
textObj.x = boxX + padding;
177205
textObj.y = boxY + padding;
178-
179-
this.tooltipContainer.addChild(bg);
180-
this.tooltipContainer.addChild(textObj);
181206
}
182207

183208
update() {
@@ -192,6 +217,9 @@ export class TimeGraphChartRichCursor extends TimeGraphChartLayer {
192217
if (this._mouseMoveHandler) {
193218
this.removeOnCanvasEvent('pointermove', this._mouseMoveHandler);
194219
}
220+
if (this._pointerLeaveHandler) {
221+
this.removeOnCanvasEvent('pointerleave', this._pointerLeaveHandler);
222+
}
195223
super.destroy();
196224
}
197225
}

0 commit comments

Comments
 (0)