import { Ref } from "vue"; import { createTypedChart } from "vue-chartjs"; import { LineController } from "chart.js"; const lineAlign = 8; type GetMapPlugin = { mapX: Ref>; }; class LineWithLineController extends LineController { static override id = "line-with-line"; private getMapX(): Array { if (!this.chart.config.plugins) { return []; } if (this.chart.isZoomedOrPanned()) { return []; } for (const i in this.chart.config.plugins) { if (this.chart.config.plugins[i].id != "yandexMapLine") { continue; } // @ts-expect-error — chart.js plugin config is loosely typed; cast via known shape const pluginProps: GetMapPlugin = this.chart.config.plugins[i]; return pluginProps.mapX.value; } return []; } public override draw() { super.draw(); const ctx = this.chart.ctx; const topY = this.chart.scales.linearAxis.top; const bottomY = this.chart.scales.linearAxis.bottom; const xVertical = this.getMapX(); // The X axis is a time scale with irregular intervals (pauses), so the // pixel position must come from Chart.js itself, not from index * zoom. const meta = this.chart.getDatasetMeta(0); for (const i in xVertical) { const idx = Number(xVertical[i]); const point = meta.data[idx] as { x?: number } | undefined; const x = point?.x ?? NaN; if (!Number.isFinite(x)) { continue; } ctx.save(); ctx.beginPath(); ctx.moveTo(x, topY); ctx.lineTo(x, bottomY); ctx.lineWidth = 3; ctx.strokeStyle = "#666"; ctx.stroke(); ctx.restore(); } if (this.chart?.tooltip && this.chart.tooltip.opacity > 0) { let x = this.chart.tooltip.x - lineAlign; if (this.chart.tooltip.xAlign === "right") { x = this.chart.tooltip.x + this.chart.tooltip.width + lineAlign; } // draw line ctx.save(); ctx.beginPath(); ctx.moveTo(x, topY); ctx.lineTo(x, bottomY); ctx.lineWidth = 1; ctx.strokeStyle = "#07C"; ctx.stroke(); ctx.restore(); } } } const LineWithLineChart = createTypedChart( "line-with-line" as "line", LineWithLineController, ); export default LineWithLineChart;