From b749fa90f23d187d5428de1a2d321cc6497b6667 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 9 Mar 2026 10:43:56 -0500 Subject: fix(app): scroll jitter/loop --- packages/ui/src/components/scroll-view.test.ts | 19 ++++++++++++++ packages/ui/src/components/scroll-view.tsx | 36 +++++++++++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 packages/ui/src/components/scroll-view.test.ts (limited to 'packages/ui/src') diff --git a/packages/ui/src/components/scroll-view.test.ts b/packages/ui/src/components/scroll-view.test.ts new file mode 100644 index 000000000..d28b51fea --- /dev/null +++ b/packages/ui/src/components/scroll-view.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, test } from "bun:test" +import { scrollKey } from "./scroll-view" + +describe("scrollKey", () => { + test("maps plain navigation keys", () => { + expect(scrollKey({ key: "PageDown", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false })).toBe( + "page-down", + ) + expect(scrollKey({ key: "ArrowUp", altKey: false, ctrlKey: false, metaKey: false, shiftKey: false })).toBe("up") + }) + + test("ignores modified keybinds", () => { + expect( + scrollKey({ key: "ArrowDown", altKey: false, ctrlKey: false, metaKey: true, shiftKey: false }), + ).toBeUndefined() + expect(scrollKey({ key: "PageUp", altKey: false, ctrlKey: true, metaKey: false, shiftKey: false })).toBeUndefined() + expect(scrollKey({ key: "End", altKey: false, ctrlKey: false, metaKey: false, shiftKey: true })).toBeUndefined() + }) +}) diff --git a/packages/ui/src/components/scroll-view.tsx b/packages/ui/src/components/scroll-view.tsx index 52ed39a46..c3d878af6 100644 --- a/packages/ui/src/components/scroll-view.tsx +++ b/packages/ui/src/components/scroll-view.tsx @@ -6,6 +6,25 @@ export interface ScrollViewProps extends ComponentProps<"div"> { orientation?: "vertical" | "horizontal" // currently only vertical is fully implemented for thumb } +export const scrollKey = (event: Pick) => { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return + + switch (event.key) { + case "PageDown": + return "page-down" + case "PageUp": + return "page-up" + case "Home": + return "home" + case "End": + return "end" + case "ArrowUp": + return "up" + case "ArrowDown": + return "down" + } +} + export function ScrollView(props: ScrollViewProps) { const i18n = useI18n() const merged = mergeProps({ orientation: "vertical" }, props) @@ -133,31 +152,34 @@ export function ScrollView(props: ScrollViewProps) { return } + const next = scrollKey(e) + if (!next) return + const scrollAmount = viewportRef.clientHeight * 0.8 const lineAmount = 40 - switch (e.key) { - case "PageDown": + switch (next) { + case "page-down": e.preventDefault() viewportRef.scrollBy({ top: scrollAmount, behavior: "smooth" }) break - case "PageUp": + case "page-up": e.preventDefault() viewportRef.scrollBy({ top: -scrollAmount, behavior: "smooth" }) break - case "Home": + case "home": e.preventDefault() viewportRef.scrollTo({ top: 0, behavior: "smooth" }) break - case "End": + case "end": e.preventDefault() viewportRef.scrollTo({ top: viewportRef.scrollHeight, behavior: "smooth" }) break - case "ArrowUp": + case "up": e.preventDefault() viewportRef.scrollBy({ top: -lineAmount, behavior: "smooth" }) break - case "ArrowDown": + case "down": e.preventDefault() viewportRef.scrollBy({ top: lineAmount, behavior: "smooth" }) break -- cgit v1.2.3