+32
-33
src/components/sticky.tsx
+32
-33
src/components/sticky.tsx
···
4
4
const [filterStuck, setFilterStuck] = createSignal(false);
5
5
6
6
return (
7
-
<div
8
-
ref={(node) => {
9
-
onMount(() => {
10
-
let ticking = false;
11
-
const tick = () => {
12
-
const topPx = parseFloat(getComputedStyle(node).top);
13
-
const { top } = node.getBoundingClientRect();
14
-
setFilterStuck(top <= topPx + 0.5);
15
-
ticking = false;
16
-
};
7
+
<>
8
+
<div
9
+
ref={(trigger) => {
10
+
onMount(() => {
11
+
const observer = new IntersectionObserver(
12
+
([entry]) => setFilterStuck(!entry.isIntersecting),
13
+
{
14
+
rootMargin: "-8px 0px 0px 0px",
15
+
threshold: 0,
16
+
},
17
+
);
17
18
18
-
const onScroll = () => {
19
-
if (!ticking) {
20
-
ticking = true;
21
-
requestAnimationFrame(tick);
22
-
}
23
-
};
19
+
observer.observe(trigger);
24
20
25
-
window.addEventListener("scroll", onScroll, { passive: true });
26
-
27
-
tick();
28
-
29
-
onCleanup(() => {
30
-
window.removeEventListener("scroll", onScroll);
21
+
onCleanup(() => {
22
+
observer.unobserve(trigger);
23
+
observer.disconnect();
24
+
});
31
25
});
32
-
});
33
-
}}
34
-
class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors"
35
-
classList={{
36
-
"bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md":
37
-
filterStuck(),
38
-
"bg-transparent border-transparent shadow-none": !filterStuck(),
39
-
}}
40
-
>
41
-
{props.children}
42
-
</div>
26
+
}}
27
+
class="pointer-events-none h-0"
28
+
aria-hidden="true"
29
+
/>
30
+
31
+
<div
32
+
class="sticky top-2 z-10 flex flex-col items-center justify-center gap-2 rounded-lg p-3 transition-colors"
33
+
classList={{
34
+
"bg-neutral-50 dark:bg-dark-300 border-[0.5px] border-neutral-300 dark:border-neutral-700 shadow-md":
35
+
filterStuck(),
36
+
"bg-transparent border-transparent shadow-none": !filterStuck(),
37
+
}}
38
+
>
39
+
{props.children}
40
+
</div>
41
+
</>
43
42
);
44
43
};