mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
fix(editor): show the scrollbar thumb over overview marks (#1031)
The overview lane lies over the vertical scrollbar, so a mark the height of the lane (a new file added to git is one green VCS mark) hid the native thumb completely. The lane now draws the thumb itself, above its marks and without taking the pointer, at the position of the native one (scrollbarThumb, inset by its 2px border); it switches to the hover color while the pointer is on the thumb. The native thumb is made transparent in editors that have the lane, so the two don't add up, and still takes the drag. Editors without the lane (notebook cells, previews) keep the native thumb. Fixes #1014 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,3 +28,22 @@ test("the scrollbar thumb drags even where overview marks cover it (#752)", asyn
|
||||
await page.mouse.move(x, box.y + box.height - 20);
|
||||
await expect(editor.locator(".cm-openidea-overview")).not.toHaveClass(/cm-openidea-overview-onThumb/);
|
||||
});
|
||||
|
||||
test("the scrollbar thumb shows over a mark the height of the lane (#1014)", async ({ workbench: page }) => {
|
||||
const editor = await openFromTree(page, "build.gradle.kts");
|
||||
await editor.locator(".cm-content").click();
|
||||
await page.keyboard.press("Meta+a");
|
||||
await page.keyboard.insertText(Array.from({ length: 400 }, (_, i) => `val mark${i} = ${i}`).join("\n"));
|
||||
await page.keyboard.press("Meta+f");
|
||||
await page.getByRole("textbox", { name: "Find" }).fill("mark");
|
||||
await expect(editor.locator(".cm-openidea-overviewMark").first()).toBeVisible();
|
||||
const thumb = editor.locator(".cm-openidea-overviewThumb");
|
||||
await expect(thumb).toBeVisible();
|
||||
// Drawn last in the lane, so it paints over every mark.
|
||||
expect(await thumb.evaluate((t) => t.nextElementSibling)).toBeNull();
|
||||
const scroller = editor.locator(".cm-scroller");
|
||||
const top = async () => (await thumb.boundingBox())!.y;
|
||||
const before = await top();
|
||||
await scroller.evaluate((s) => (s.scrollTop = s.scrollHeight));
|
||||
await expect.poll(top).toBeGreaterThan(before + 100);
|
||||
});
|
||||
|
||||
@@ -64,11 +64,18 @@ class OverviewLane {
|
||||
private lastKey = "";
|
||||
/** The last pointer position over the editor, to re-check the thumb when it scrolls. */
|
||||
private pointer: { x: number; y: number } | null = null;
|
||||
/**
|
||||
* The scrollbar thumb drawn over the marks: the native one sits under the lane, so a mark
|
||||
* the height of the lane (a new file) would hide it (#1014).
|
||||
*/
|
||||
private thumb: HTMLElement;
|
||||
|
||||
constructor(private view: EditorView) {
|
||||
this.dom = document.createElement("div");
|
||||
this.dom.className = "cm-openidea-overview";
|
||||
this.dom.setAttribute("aria-hidden", "true");
|
||||
this.thumb = document.createElement("div");
|
||||
this.thumb.className = "cm-openidea-overviewThumb";
|
||||
this.dom.addEventListener("mousedown", this.onMouseDown);
|
||||
view.dom.addEventListener("mousemove", this.onPointerMove);
|
||||
view.dom.addEventListener("mouseleave", this.onPointerLeave);
|
||||
@@ -153,9 +160,31 @@ class OverviewLane {
|
||||
c.className = "cm-openidea-overviewCaret";
|
||||
c.style.top = `${caret}${unit}`;
|
||||
frag.appendChild(c);
|
||||
frag.appendChild(this.thumb);
|
||||
this.dom.replaceChildren(frag);
|
||||
},
|
||||
});
|
||||
this.scheduleThumb();
|
||||
}
|
||||
|
||||
private scheduleThumb() {
|
||||
this.view.requestMeasure({
|
||||
key: this.thumb,
|
||||
read: (view) => {
|
||||
const s = view.scrollDOM;
|
||||
const thumb = scrollbarThumb(s.scrollTop, s.scrollHeight, s.clientHeight);
|
||||
// The lane starts at the top of the editor, the scrollbar at the top of the scroller.
|
||||
const offset = s.getBoundingClientRect().top - this.dom.getBoundingClientRect().top;
|
||||
// Inset by the thumb's 2px transparent border (global.css).
|
||||
return thumb && { top: thumb.top + offset + 2, height: thumb.bottom - thumb.top - 4 };
|
||||
},
|
||||
write: (thumb) => {
|
||||
this.thumb.style.display = thumb ? "" : "none";
|
||||
if (!thumb) return;
|
||||
this.thumb.style.top = `${thumb.top}px`;
|
||||
this.thumb.style.height = `${thumb.height}px`;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,6 +216,7 @@ class OverviewLane {
|
||||
};
|
||||
|
||||
private onScroll = () => {
|
||||
this.scheduleThumb();
|
||||
if (this.pointer) this.updateThumbPassThrough();
|
||||
};
|
||||
|
||||
@@ -218,4 +248,7 @@ class OverviewLane {
|
||||
}
|
||||
}
|
||||
|
||||
export const overviewLane: Extension = ViewPlugin.fromClass(OverviewLane);
|
||||
export const overviewLane: Extension = [
|
||||
ViewPlugin.fromClass(OverviewLane),
|
||||
EditorView.editorAttributes.of({ class: "cm-openidea-withOverview" }),
|
||||
];
|
||||
|
||||
@@ -285,6 +285,17 @@ export const openideaTheme = EditorView.theme({
|
||||
".cm-openidea-overviewMark-thin": { left: "4px", width: "4px", minHeight: "2px" },
|
||||
// The scrollbar thumb wins over the marks under it (cm/lanes.ts, #752).
|
||||
".cm-openidea-overview-onThumb .cm-openidea-overviewMark": { pointerEvents: "none" },
|
||||
// The lane draws the thumb over its marks (#1014); the native one only takes the drag.
|
||||
"&.cm-openidea-withOverview .cm-scroller::-webkit-scrollbar-thumb:vertical": { backgroundColor: "transparent" },
|
||||
".cm-openidea-overviewThumb": {
|
||||
position: "absolute",
|
||||
right: "2px",
|
||||
width: "6px",
|
||||
borderRadius: "3px",
|
||||
background: "var(--scrollbar-thumb)",
|
||||
pointerEvents: "none",
|
||||
},
|
||||
".cm-openidea-overview-onThumb .cm-openidea-overviewThumb": { background: "var(--scrollbar-thumb-hover)" },
|
||||
".cm-openidea-overviewCaret": {
|
||||
position: "absolute",
|
||||
left: "2px",
|
||||
|
||||
Reference in New Issue
Block a user