fix: edge tooltips were hidden inside the box (#5)

* fix: edge tooltips were hidden inside the box

* Update README.md
This commit is contained in:
2026-04-12 20:51:56 +02:00
committed by GitHub
parent 80ba688f41
commit 26cc1dac8a
3 changed files with 121 additions and 32 deletions

View File

@@ -368,4 +368,4 @@ npm run check:pages # pages (astro check + tsc)
- [ ] Refine the status page to look... well... less IA generated. - [ ] Refine the status page to look... well... less IA generated.
- [ ] Initial support for incident management (manual status overrides, incident timeline). - [ ] Initial support for incident management (manual status overrides, incident timeline).
- [x] Branded status page (simple custom banner). - [x] Branded status page (simple custom banner).
- [] Add support for notifications other than webhooks. - [ ] Add support for notifications other than webhooks.

View File

@@ -61,6 +61,8 @@ function getBarColor(uptimePercent: number | undefined): string {
gap: 1px; gap: 1px;
width: 100%; width: 100%;
height: 14px; height: 14px;
position: relative;
overflow: visible;
} }
.bar { .bar {
@@ -93,13 +95,17 @@ function getBarColor(uptimePercent: number | undefined): string {
background: var(--no-data); background: var(--no-data);
} }
/* Tooltip */ /* Tooltip - positioned below to avoid conflict with bar labels */
.bar {
position: relative;
}
.bar::before { .bar::before {
content: attr(data-tooltip); content: attr(data-tooltip);
position: absolute; position: absolute;
bottom: 100%; top: calc(100% + 8px);
left: 50%; left: 50%;
transform: translateX(-50%) translateY(-8px); transform: translateX(-50%);
background: var(--text); background: var(--text);
color: var(--bg); color: var(--bg);
padding: 4px 8px; padding: 4px 8px;
@@ -112,16 +118,56 @@ function getBarColor(uptimePercent: number | undefined): string {
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
pointer-events: none; pointer-events: none;
z-index: 1000; z-index: 1000;
/* Prevent tooltip overflow */
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
/* Ensure tooltip stays within screen bounds */
left: 50%;
transform: translateX(-50%);
/* Constrain to viewport */
max-width: min(200px, calc(100vw - 32px));
}
/* For left-edge bars, prevent left overflow */
.bar:nth-child(1)::before,
.bar:nth-child(2)::before,
.bar:nth-child(3)::before,
.bar:nth-child(4)::before,
.bar:nth-child(5)::before,
.bar:nth-child(6)::before,
.bar:nth-child(7)::before,
.bar:nth-child(8)::before,
.bar:nth-child(9)::before,
.bar:nth-child(10)::before {
left: 0;
transform: translateX(0);
}
/* For right-edge bars, prevent right overflow */
.bar:nth-last-child(1)::before,
.bar:nth-last-child(2)::before,
.bar:nth-last-child(3)::before,
.bar:nth-last-child(4)::before,
.bar:nth-last-child(5)::before,
.bar:nth-last-child(6)::before,
.bar:nth-last-child(7)::before,
.bar:nth-last-child(8)::before,
.bar:nth-last-child(9)::before,
.bar:nth-last-child(10)::before {
left: auto;
right: 0;
transform: translateX(0);
} }
.bar::after { .bar::after {
content: ''; content: '';
position: absolute; position: absolute;
bottom: 100%; top: calc(100% + 2px);
left: 50%; left: 50%;
transform: translateX(-50%) translateY(-2px); transform: translateX(-50%);
border: 4px solid transparent; border: 4px solid transparent;
border-top-color: var(--text); border-bottom-color: var(--text);
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: opacity 0.2s ease; transition: opacity 0.2s ease;
@@ -129,6 +175,21 @@ function getBarColor(uptimePercent: number | undefined): string {
z-index: 1000; z-index: 1000;
} }
/* Adjust arrow position for edge bars */
.bar:nth-child(1)::after,
.bar:nth-child(2)::after,
.bar:nth-child(3)::after,
.bar:nth-child(4)::after,
.bar:nth-child(5)::after,
.bar:nth-child(6)::after,
.bar:nth-child(7)::after,
.bar:nth-child(8)::after,
.bar:nth-child(9)::after,
.bar:nth-child(10)::after {
left: 8px;
transform: translateX(0);
}
.bar:hover::before, .bar:hover::before,
.bar:focus::before, .bar:focus::before,
.bar:hover::after, .bar:hover::after,
@@ -136,12 +197,4 @@ function getBarColor(uptimePercent: number | undefined): string {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} }
.bar-labels {
display: flex;
justify-content: space-between;
font-size: 9px;
color: var(--text-dim);
margin-top: 3px;
}
</style> </style>

View File

@@ -79,18 +79,18 @@ function tooltipPlugin(strokeColor: string): uPlot.Plugin {
tooltipElement = document.createElement('div'); tooltipElement = document.createElement('div');
tooltipElement.className = 'chart-tooltip'; tooltipElement.className = 'chart-tooltip';
tooltipElement.style.cssText = ` tooltipElement.style.cssText = `
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
background: rgba(15, 23, 42, 0.95); background: rgba(15, 23, 42, 0.95);
border: 1px solid ${strokeColor}; border: 1px solid ${strokeColor};
color: #e2e8f0; color: #e2e8f0;
padding: 4px 8px; padding: 4px 8px;
border-radius: 4px; border-radius: 4px;
font: 500 10px 'Geist Mono', monospace; font: 500 10px 'Geist Mono', monospace;
display: none; display: none;
white-space: nowrap; white-space: nowrap;
z-index: 10; z-index: 10;
`; `;
// Cast needed: @cloudflare/workers-types overrides DOM append() signature // Cast needed: @cloudflare/workers-types overrides DOM append() signature
(over as ParentNode).append(tooltipElement); (over as ParentNode).append(tooltipElement);
@@ -131,18 +131,54 @@ function tooltipPlugin(strokeColor: string): uPlot.Plugin {
const msString = Math.round(yValue) + ' ms'; const msString = Math.round(yValue) + ' ms';
tooltipElement.textContent = `${timeString} ${msString}`; tooltipElement.textContent = `${timeString} ${msString}`;
// Position tooltip, flipping side if near right edge // Position tooltip, ensuring it stays within chart bounds
const tipWidth = tooltipElement.offsetWidth; const tipWidth = tooltipElement.offsetWidth;
const tipHeight = tooltipElement.offsetHeight;
const plotWidth = over.clientWidth; const plotWidth = over.clientWidth;
const plotHeight = over.clientHeight;
const shiftX = 12; const shiftX = 12;
const shiftY = -10; const shiftY = -10;
let posLeft = left + shiftX;
if (posLeft + tipWidth > plotWidth) { // Calculate horizontal position - choose side with more space
let posLeft: number;
const spaceRight = plotWidth - (left + shiftX);
const spaceLeft = left - shiftX;
// Default to right side if there's enough space
if (spaceRight >= tipWidth) {
posLeft = left + shiftX;
}
// Try left side if there's more space there
else if (spaceLeft >= tipWidth) {
posLeft = left - tipWidth - shiftX; posLeft = left - tipWidth - shiftX;
} }
// Not enough space on either side, choose the better option
else {
// If right side has more space than left, use right side clamped
if (spaceRight > spaceLeft) {
posLeft = plotWidth - tipWidth;
} else {
// Use left side clamped
posLeft = 0;
}
}
// Ensure we don't go outside bounds
posLeft = Math.max(0, Math.min(posLeft, plotWidth - tipWidth));
// Calculate vertical position
let posTop = (top ?? 0) + shiftY;
// Check if tooltip would overflow top edge
if (posTop < 0) {
posTop = 0;
}
// Check if tooltip would overflow bottom edge
else if (posTop + tipHeight > plotHeight) {
posTop = plotHeight - tipHeight;
}
tooltipElement.style.left = posLeft + 'px'; tooltipElement.style.left = posLeft + 'px';
tooltipElement.style.top = (top ?? 0) + shiftY + 'px'; tooltipElement.style.top = posTop + 'px';
}, },
], ],
}, },