fix: stabilize popover positioning and prevent viewport overflow

- Switch popover from position:absolute to position:fixed to avoid
  scroll-offset math and simplify viewport clamping.
- Add max-height + overflow-y:auto so the popover scrolls internally
  instead of overflowing the viewport.
- Remove the reposition call after picker opens — this was causing
  the popover to flash and jump when the picker changed its height.
- Improve edge clamping: if neither above nor below fits, clamp to
  viewport bottom rather than flipping unpredictably.
- Remove the CSS arrow (::before) which was mispositioned when the
  popover appeared above the badge or near edges.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 17:26:12 -04:00
parent 84621484da
commit d5f8c2e480
2 changed files with 19 additions and 30 deletions

View File

@@ -86,14 +86,16 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) {
/* --- Color popover --- */ /* --- Color popover --- */
.rt-color-popover { .rt-color-popover {
position: absolute; position: fixed;
z-index: 10000; z-index: 10000;
background: #fff; background: #fff;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 6px; border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
padding: 12px; padding: 12px;
width: 260px; width: 260px;
max-height: calc(100vh - 20px);
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px; font-size: 13px;
color: #1a1a1a; color: #1a1a1a;
@@ -104,20 +106,6 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) {
display: block; display: block;
} }
/* Popover arrow */
.rt-color-popover::before {
content: "";
position: absolute;
top: -6px;
left: 16px;
width: 10px;
height: 10px;
background: #fff;
border-left: 1px solid #ccc;
border-top: 1px solid #ccc;
transform: rotate(45deg);
}
.rt-popover-header { .rt-popover-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -275,12 +263,6 @@ html.rt-enabled .thing.link .flat-list li:has(> .comments) {
color: #e0e0e0; color: #e0e0e0;
} }
.rt-color-popover.rt-dark::before {
background: #2a2a2a;
border-left-color: #555;
border-top-color: #555;
}
.rt-color-popover.rt-dark .rt-popover-header { .rt-color-popover.rt-dark .rt-popover-header {
border-bottom-color: #444; border-bottom-color: #444;
} }

View File

@@ -161,7 +161,6 @@
row.hex.classList.remove("rt-input-error"); row.hex.classList.remove("rt-input-error");
updatePreview(); updatePreview();
}); });
if (currentAnchor) positionPopover(currentAnchor);
} }
function closePicker() { function closePicker() {
@@ -238,21 +237,29 @@
function positionPopover(anchorEl) { function positionPopover(anchorEl) {
var rect = anchorEl.getBoundingClientRect(); var rect = anchorEl.getBoundingClientRect();
var gap = 6; var gap = 6;
var pad = 10;
popoverEl.style.visibility = "hidden"; popoverEl.style.visibility = "hidden";
popoverEl.classList.add("rt-popover-visible"); popoverEl.classList.add("rt-popover-visible");
var popRect = popoverEl.getBoundingClientRect(); var popRect = popoverEl.getBoundingClientRect();
var top = rect.bottom + window.scrollY + gap;
var left = rect.left + window.scrollX;
if (rect.bottom + gap + popRect.height > window.innerHeight) { var top = rect.bottom + gap;
top = rect.top + window.scrollY - popRect.height - gap; var left = rect.left;
if (top + popRect.height > window.innerHeight - pad) {
var aboveTop = rect.top - popRect.height - gap;
if (aboveTop >= pad) {
top = aboveTop;
} else {
top = Math.max(pad, window.innerHeight - popRect.height - pad);
}
} }
var maxLeft = document.documentElement.scrollWidth - popRect.width - 8; if (left + popRect.width > window.innerWidth - pad) {
if (left > maxLeft) left = maxLeft; left = window.innerWidth - popRect.width - pad;
if (left < 8) left = 8; }
if (left < pad) left = pad;
popoverEl.style.top = top + "px"; popoverEl.style.top = top + "px";
popoverEl.style.left = left + "px"; popoverEl.style.left = left + "px";