Scroll to the reply when accessing it from profile
This commit is contained in:
parent
a9b0702c68
commit
c793203d9f
1 changed files with 56 additions and 0 deletions
|
|
@ -255,6 +255,61 @@
|
||||||
return clearHighlights;
|
return clearHighlights;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Arriving with a #post-… hash (a reply link): the target only exists once
|
||||||
|
// the replies have loaded, so scroll when it appears, once per navigation.
|
||||||
|
let scrolledHashFor = "";
|
||||||
|
$effect(() => {
|
||||||
|
const hash = page.url.hash;
|
||||||
|
allPosts; // Re-check as replies stream in
|
||||||
|
if (!threadEl || !hash.startsWith("#post-")) return;
|
||||||
|
const key = `${page.params.id}|${hash}`;
|
||||||
|
if (scrolledHashFor === key) return;
|
||||||
|
const el = document.getElementById(hash.slice(1));
|
||||||
|
if (!el) return;
|
||||||
|
scrolledHashFor = key;
|
||||||
|
scrollToPost(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
// On desktop the thread scrolls inside <main> under a sticky title with a
|
||||||
|
// fade below it, so place the post under both; elsewhere the scroll margin
|
||||||
|
// is enough.
|
||||||
|
const HEADER_FADE = 32; // the h-8 gradient
|
||||||
|
const HEADER_GAP = 12;
|
||||||
|
function scrollToPost(el: HTMLElement) {
|
||||||
|
const main = document.querySelector("main");
|
||||||
|
const header = threadEl?.querySelector("[data-thread-header]");
|
||||||
|
if (!main || !header || !window.matchMedia("(min-width: 768px)").matches) {
|
||||||
|
el.scrollIntoView({ block: "start" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const headerBottom =
|
||||||
|
main.getBoundingClientRect().top + header.getBoundingClientRect().height;
|
||||||
|
const offset = headerBottom + HEADER_FADE + HEADER_GAP;
|
||||||
|
main.scrollTop += el.getBoundingClientRect().top - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In-thread quote links ("… said") get the same placement instead of the
|
||||||
|
// browser's default anchor jump.
|
||||||
|
$effect(() => {
|
||||||
|
const root = threadEl;
|
||||||
|
if (!root) return;
|
||||||
|
const onClick = (e: MouseEvent) => {
|
||||||
|
if (e.defaultPrevented || e.button !== 0) return;
|
||||||
|
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
||||||
|
const a = (e.target as Element).closest("a[href^='#post-']");
|
||||||
|
if (!a) return;
|
||||||
|
const hash = a.getAttribute("href") ?? "";
|
||||||
|
const el = document.getElementById(hash.slice(1));
|
||||||
|
if (!el) return;
|
||||||
|
e.preventDefault();
|
||||||
|
scrolledHashFor = `${page.params.id}|${hash}`;
|
||||||
|
goto(hash, { noScroll: true });
|
||||||
|
scrollToPost(el);
|
||||||
|
};
|
||||||
|
root.addEventListener("click", onClick);
|
||||||
|
return () => root.removeEventListener("click", onClick);
|
||||||
|
});
|
||||||
|
|
||||||
// A thread belongs to its own group; make that the active group so chat and
|
// A thread belongs to its own group; make that the active group so chat and
|
||||||
// replies target the right room.
|
// replies target the right room.
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|
@ -483,6 +538,7 @@
|
||||||
<div class="flex items-start gap-6" bind:this={threadEl}>
|
<div class="flex items-start gap-6" bind:this={threadEl}>
|
||||||
<div class="min-w-0 flex-1 {!scrubberVisible ? 'md:pr-18' : ''}">
|
<div class="min-w-0 flex-1 {!scrubberVisible ? 'md:pr-18' : ''}">
|
||||||
<div
|
<div
|
||||||
|
data-thread-header
|
||||||
class="relative z-10 bg-white pb-1 md:sticky md:-top-2 md:-mx-10 md:-mt-6 md:px-10 md:pt-6 md:pb-3 dark:bg-neutral-900"
|
class="relative z-10 bg-white pb-1 md:sticky md:-top-2 md:-mx-10 md:-mt-6 md:px-10 md:pt-6 md:pb-3 dark:bg-neutral-900"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue