(input: { tab: () => string; view: ReturnType<typeof useSessionLayout>["view"] })
| 56 | type ScrollPos = { x: number; y: number } |
| 57 | |
| 58 | function createScrollSync(input: { tab: () => string; view: ReturnType<typeof useSessionLayout>["view"] }) { |
| 59 | let scroll: HTMLDivElement | undefined |
| 60 | let scrollFrame: number | undefined |
| 61 | let restoreFrame: number | undefined |
| 62 | let pending: ScrollPos | undefined |
| 63 | const [code, setCode] = createSignal<HTMLElement[]>([]) |
| 64 | |
| 65 | const getCode = () => { |
| 66 | const el = scroll |
| 67 | if (!el) return [] |
| 68 | |
| 69 | const host = el.querySelector("diffs-container") |
| 70 | if (!(host instanceof HTMLElement)) return [] |
| 71 | |
| 72 | const root = host.shadowRoot |
| 73 | if (!root) return [] |
| 74 | |
| 75 | return Array.from(root.querySelectorAll("[data-code]")).filter( |
| 76 | (node): node is HTMLElement => node instanceof HTMLElement && node.clientWidth > 0, |
| 77 | ) |
| 78 | } |
| 79 | |
| 80 | const save = (next: ScrollPos) => { |
| 81 | pending = next |
| 82 | if (scrollFrame !== undefined) return |
| 83 | |
| 84 | scrollFrame = requestAnimationFrame(() => { |
| 85 | scrollFrame = undefined |
| 86 | |
| 87 | const out = pending |
| 88 | pending = undefined |
| 89 | if (!out) return |
| 90 | |
| 91 | input.view().setScroll(input.tab(), out) |
| 92 | }) |
| 93 | } |
| 94 | |
| 95 | const onCodeScroll = (event: Event) => { |
| 96 | const el = scroll |
| 97 | if (!el) return |
| 98 | |
| 99 | const target = event.currentTarget |
| 100 | if (!(target instanceof HTMLElement)) return |
| 101 | |
| 102 | save({ |
| 103 | x: target.scrollLeft, |
| 104 | y: el.scrollTop, |
| 105 | }) |
| 106 | } |
| 107 | |
| 108 | const sync = () => { |
| 109 | const next = getCode() |
| 110 | const current = code() |
| 111 | if (next.length === current.length && next.every((el, i) => el === current[i])) return |
| 112 | setCode(next) |
| 113 | } |
| 114 | |
| 115 | const restore = () => { |
no test coverage detected