diff --git a/resources/views/livewire/traffic/_sparkline.blade.php b/resources/views/livewire/traffic/_sparkline.blade.php index 161dd2abb4..739d7aeaf3 100644 --- a/resources/views/livewire/traffic/_sparkline.blade.php +++ b/resources/views/livewire/traffic/_sparkline.blade.php @@ -17,6 +17,10 @@ @param ?string $event Livewire event to listen on for updates (optional) @param ?string $key payload key holding the numeric array (required with $event) @param ?array $categories initial x-axis timestamps (defaults to $series buckets) + + The hover tooltip renders in a fixed element on , below the spark, so it does not + cover the line. The stat tile grids clip overflow, so ApexCharts' own tooltip element + (inside the chart) cannot sit below the chart. --}} @php $initial = $initial ?? []; @@ -43,6 +47,23 @@ x-data="{ chart: null, refreshCleanup: null, + tip: null, + hideTipHandler: null, + showTip(html, pointX) { + if (!this.tip) { + this.tip = document.createElement('div'); + this.tip.className = 'pointer-events-none fixed z-50'; + document.body.appendChild(this.tip); + } + this.tip.innerHTML = html; + this.tip.hidden = false; + const rect = $el.getBoundingClientRect(); + const width = this.tip.offsetWidth; + const left = Math.min(Math.max(rect.left + pointX - width / 2, 8), window.innerWidth - width - 8); + this.tip.style.left = `${left}px`; + this.tip.style.top = `${rect.bottom + 8}px`; + }, + hideTip() { if (this.tip) { this.tip.hidden = true; } }, accent() { const c = @js($color); if (c) { return c; } return getComputedStyle(document.documentElement).getPropertyValue('{{ $colorVar }}').trim() || '#3b82f6'; }, muted() { return getComputedStyle(document.documentElement).getPropertyValue('--chart-geo-empty').trim() || 'rgba(128,128,128,0.45)'; }, isFlat(a) { return !Array.isArray(a) || a.length === 0 || a.every(v => !Number(v)); }, @@ -99,14 +120,18 @@ custom: ({ series, dataPointIndex, w }) => { const value = series[0][dataPointIndex]; const timestamp = w.globals.seriesX[0][dataPointIndex]; - return `
+ this.showTip(`
${@js($label)}: ${formatValue(value)}
${window.chartTooltipTime(timestamp, { seconds: false })} -
`; +
`, w.globals.translateX + w.globals.seriesXvalues[0][dataPointIndex]); + return ''; }, }, }); this.chart.render(); + this.hideTipHandler = () => this.hideTip(); + $el.addEventListener('mouseleave', this.hideTipHandler); + window.addEventListener('scroll', this.hideTipHandler, { capture: true, passive: true }); const event = @js($event); const key = @js($key); if (event && key) { @@ -123,5 +148,7 @@ destroy() { this.refreshCleanup?.(); this.chart?.destroy(); + this.tip?.remove(); + window.removeEventListener('scroll', this.hideTipHandler, { capture: true }); }, }">