diff options
| author | Adam <[email protected]> | 2026-01-19 11:44:20 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-01-19 11:44:20 -0600 |
| commit | 092428633fe05b33c26a94549d6e65d2235da514 (patch) | |
| tree | bf95acb39d959ba576706bbfe86fd16623402dc3 | |
| parent | fc50b2962c24dc37fa131759cd56460fbc1f43fa (diff) | |
| download | opencode-092428633fe05b33c26a94549d6e65d2235da514.tar.gz opencode-092428633fe05b33c26a94549d6e65d2235da514.zip | |
fix(app): layout jumping
| -rw-r--r-- | packages/ui/src/components/list.tsx | 38 |
1 files changed, 32 insertions, 6 deletions
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx index b8a8f7460..874638c5a 100644 --- a/packages/ui/src/components/list.tsx +++ b/packages/ui/src/components/list.tsx @@ -35,6 +35,25 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void }) mouseActive: false, }) + const scrollIntoView = (container: HTMLDivElement, node: HTMLElement, block: "center" | "nearest") => { + const containerRect = container.getBoundingClientRect() + const nodeRect = node.getBoundingClientRect() + const top = nodeRect.top - containerRect.top + container.scrollTop + const bottom = top + nodeRect.height + const viewTop = container.scrollTop + const viewBottom = viewTop + container.clientHeight + const target = + block === "center" + ? top - container.clientHeight / 2 + nodeRect.height / 2 + : top < viewTop + ? top + : bottom > viewBottom + ? bottom - container.clientHeight + : viewTop + const max = Math.max(0, container.scrollHeight - container.clientHeight) + container.scrollTop = Math.max(0, Math.min(target, max)) + } + const { filter, grouped, flat, active, setActive, onKeyDown, onInput } = useFilteredList<T>(props) const searchProps = () => (typeof props.search === "object" ? props.search : {}) @@ -65,24 +84,31 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void }) ) createEffect(() => { - if (!scrollRef()) return + const scroll = scrollRef() + if (!scroll) return if (!props.current) return const key = props.key(props.current) requestAnimationFrame(() => { - const element = scrollRef()?.querySelector(`[data-key="${CSS.escape(key)}"]`) - element?.scrollIntoView({ block: "center" }) + const element = scroll.querySelector(`[data-key="${CSS.escape(key)}"]`) + if (!(element instanceof HTMLElement)) return + scrollIntoView(scroll, element, "center") }) }) createEffect(() => { const all = flat() if (store.mouseActive || all.length === 0) return + const scroll = scrollRef() + if (!scroll) return if (active() === props.key(all[0])) { - scrollRef()?.scrollTo(0, 0) + scroll.scrollTo(0, 0) return } - const element = scrollRef()?.querySelector(`[data-key="${CSS.escape(active()!)}"]`) - element?.scrollIntoView({ block: "center" }) + const key = active() + if (!key) return + const element = scroll.querySelector(`[data-key="${CSS.escape(key)}"]`) + if (!(element instanceof HTMLElement)) return + scrollIntoView(scroll, element, "center") }) createEffect(() => { |
