diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx index e3e1e5652..4e7c82d78 100644 --- a/packages/ui/src/components/code.tsx +++ b/packages/ui/src/components/code.tsx @@ -1,5 +1,6 @@ import { type FileContents, File, FileOptions, LineAnnotation, type SelectedLineRange } from "@pierre/diffs" import { ComponentProps, createEffect, createMemo, createSignal, onCleanup, onMount, Show, splitProps } from "solid-js" +import { Portal } from "solid-js/web" import { createDefaultOptions, styleVariables } from "../pierre" import { getWorkerPool } from "../pierre/worker" import { Icon } from "./icon" @@ -125,11 +126,9 @@ export function Code(props: CodeProps) { let wrapper!: HTMLDivElement let container!: HTMLDivElement let findInput: HTMLInputElement | undefined - let findBar: HTMLDivElement | undefined let findOverlay!: HTMLDivElement let findOverlayFrame: number | undefined let findOverlayScroll: HTMLElement[] = [] - let findScroll: HTMLElement | undefined let observer: MutationObserver | undefined let renderToken = 0 let selectionFrame: number | undefined @@ -159,6 +158,8 @@ export function Code(props: CodeProps) { let findMode: "highlights" | "overlay" = "overlay" let findHits: Range[] = [] + const [findPos, setFindPos] = createSignal<{ top: number; right: number }>({ top: 8, right: 8 }) + const file = createMemo( () => new File( @@ -291,23 +292,26 @@ export function Code(props: CodeProps) { setFindIndex(0) } - const getScrollParent = (el: HTMLElement): HTMLElement | null => { + const getScrollParent = (el: HTMLElement): HTMLElement | undefined => { let parent = el.parentElement while (parent) { const style = getComputedStyle(parent) if (style.overflowY === "auto" || style.overflowY === "scroll") return parent parent = parent.parentElement } - return null } const positionFindBar = () => { - if (!findBar || !wrapper) return - const scrollTop = findScroll ? findScroll.scrollTop : window.scrollY - findBar.style.position = "absolute" - findBar.style.top = `${scrollTop + 8}px` - findBar.style.right = "8px" - findBar.style.left = "" + if (typeof window === "undefined") return + + const root = getScrollParent(wrapper) ?? wrapper + const rect = root.getBoundingClientRect() + const title = parseFloat(getComputedStyle(root).getPropertyValue("--session-title-height")) + const header = Number.isNaN(title) ? 0 : title + setFindPos({ + top: Math.round(rect.top) + header - 4, + right: Math.round(window.innerWidth - rect.right) + 8, + }) } const scanFind = (root: ShadowRoot, query: string) => { @@ -426,7 +430,6 @@ export function Code(props: CodeProps) { } if (opts?.scroll && active) { scrollToRange(active) - positionFindBar() } return } @@ -435,7 +438,6 @@ export function Code(props: CodeProps) { syncOverlayScroll() if (opts?.scroll && active) { scrollToRange(active) - positionFindBar() } scheduleOverlay() } @@ -464,14 +466,12 @@ export function Code(props: CodeProps) { return } scrollToRange(active) - positionFindBar() return } clearHighlightFind() syncOverlayScroll() scrollToRange(active) - positionFindBar() scheduleOverlay() } @@ -484,11 +484,9 @@ export function Code(props: CodeProps) { findCurrent = host findTarget = host - findScroll = getScrollParent(wrapper) ?? undefined if (!findOpen()) setFindOpen(true) requestAnimationFrame(() => { applyFind({ scroll: true }) - positionFindBar() findInput?.focus() findInput?.select() }) @@ -514,18 +512,18 @@ export function Code(props: CodeProps) { createEffect(() => { if (!findOpen()) return - findScroll = getScrollParent(wrapper) ?? undefined - const target = findScroll ?? window - const handler = () => positionFindBar() - target.addEventListener("scroll", handler, { passive: true }) - window.addEventListener("resize", handler, { passive: true }) - handler() + const update = () => positionFindBar() + requestAnimationFrame(update) + window.addEventListener("resize", update, { passive: true }) + + const root = getScrollParent(wrapper) ?? wrapper + const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(() => update()) + observer?.observe(root) onCleanup(() => { - target.removeEventListener("scroll", handler) - window.removeEventListener("resize", handler) - findScroll = undefined + window.removeEventListener("resize", update) + observer?.disconnect() }) }) @@ -916,6 +914,64 @@ export function Code(props: CodeProps) { pendingSelectionEnd = false }) + const FindBar = (barProps: { class: string; style?: ComponentProps<"div">["style"] }) => ( +
e.stopPropagation()}> + + { + setFindQuery(e.currentTarget.value) + setFindIndex(0) + applyFind({ reset: true, scroll: true }) + }} + onKeyDown={(e) => { + if (e.key === "Escape") { + e.preventDefault() + closeFind() + return + } + if (e.key !== "Enter") return + e.preventDefault() + stepFind(e.shiftKey ? -1 : 1) + }} + /> +
+ {findCount() ? `${findIndex() + 1}/${findCount()}` : "0/0"} +
+
+ + +
+ +
+ ) + return (
(props: CodeProps) { }} > -
e.stopPropagation()} - > - - { - setFindQuery(e.currentTarget.value) - setFindIndex(0) - applyFind({ reset: true, scroll: true }) - }} - onKeyDown={(e) => { - if (e.key === "Escape") { - e.preventDefault() - closeFind() - return - } - if (e.key !== "Enter") return - e.preventDefault() - stepFind(e.shiftKey ? -1 : 1) + + -
- {findCount() ? `${findIndex() + 1}/${findCount()}` : "0/0"} -
-
- - -
- -
+