feature/comparison-slider #19
@@ -29,15 +29,53 @@ interface Props extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
|
||||
* Index of the section
|
||||
*/
|
||||
index?: number;
|
||||
/**
|
||||
* Callback function to notify when the title visibility status changes
|
||||
*
|
||||
* @param index - Index of the section
|
||||
* @param isPast - Whether the section is past the current scroll position
|
||||
* @param title - Snippet for a title itself
|
||||
* @returns Cleanup callback
|
||||
*/
|
||||
onTitleStatusChange?: (index: number, isPast: boolean, title?: Snippet<[{ className?: string }]>) => () => void;
|
||||
/**
|
||||
* Snippet for the section content
|
||||
*/
|
||||
children?: Snippet;
|
||||
}
|
||||
|
||||
const { class: className, title, icon, index, children }: Props = $props();
|
||||
const { class: className, title, icon, index = 0, onTitleStatusChange, children }: Props = $props();
|
||||
|
||||
let titleContainer = $state<HTMLElement>();
|
||||
const flyParams: FlyParams = { y: 0, x: -50, duration: 300, easing: cubicOut, opacity: 0.2 };
|
||||
|
||||
// Track if the user has actually scrolled away from view
|
||||
let isScrolledPast = $state(false);
|
||||
|
||||
$effect(() => {
|
||||
if (!titleContainer) {
|
||||
return;
|
||||
}
|
||||
let cleanup: ((index: number) => void) | undefined;
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
const entry = entries[0];
|
||||
const isPast = !entry.isIntersecting && entry.boundingClientRect.top < 0;
|
||||
|
||||
if (isPast !== isScrolledPast) {
|
||||
isScrolledPast = isPast;
|
||||
cleanup = onTitleStatusChange?.(index, isPast, title);
|
||||
}
|
||||
}, {
|
||||
// Set threshold to 0 to trigger exactly when the last pixel leaves
|
||||
threshold: 0,
|
||||
});
|
||||
|
||||
observer.observe(titleContainer);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
cleanup?.(index);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<section
|
||||
@@ -48,7 +86,7 @@ const flyParams: FlyParams = { y: 0, x: -50, duration: 300, easing: cubicOut, op
|
||||
in:fly={flyParams}
|
||||
out:fly={flyParams}
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-2" bind:this={titleContainer}>
|
||||
<div class="flex items-center gap-3 opacity-60">
|
||||
{#if icon}
|
||||
{@render icon({ className: 'size-4 stroke-gray-900 stroke-1' })}
|
||||
|
||||
Reference in New Issue
Block a user