Project Library title now fades when scrolling up

This commit is contained in:
corgifist 2025-07-27 23:32:45 +03:00
parent acd0762f82
commit 35f4084833
4 changed files with 44 additions and 25 deletions

View File

@ -32,6 +32,7 @@ import { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTi
import { Separator } from "@/components/ui/separator";
import StaticSidebarTrigger from "@/components/static-sidebar-trigger";
import SidebarTriggerAdjustable from "@/components/sidebar-trigger-adjustable";
import ScrollFadingTitle from "@/components/scroll-fading-title";
type SortingType = "byCreationDate"
| "byEditDate"
@ -491,8 +492,10 @@ export default function Home(): ReactNode {
<div className="p-5">
<div className="flex flex-row items-center gap-2">
<StaticSidebarTrigger />
<h2 className="font-bold break-keep text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-none">Project Library</h2>
{projects && <Label className="text-muted-foreground text-sm">(Found {projects.length} projects)</Label>}
<ScrollFadingTitle className="flex flex-row items-center gap-2">
<h2 className="font-bold break-keep text-xl sm:text-2xl md:text-3xl lg:text-4xl leading-none">Project Library</h2>
{projects && <Label className="text-muted-foreground text-sm">(Found {projects.length} projects)</Label>}
</ScrollFadingTitle>
</div>
<div className="flex flex-col sticky top-safe bg-background gap-2 mt-2 pb-2 pt-2 p-5 w-[100% + 5 * var(--spacing)] z-10 -mx-5">
<SidebarTriggerAdjustable>

View File

@ -0,0 +1,24 @@
import { ComponentProps, useEffect, useRef, useState } from "react";
const easeFade = (x: number) => (
x === 0 ? 0 : Math.pow(2, 10 * x - 10)
);
export const ScrollFadingTitle = (props: ComponentProps<"div">) => {
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleScroll = () => {
if (!elementRef.current) return;
const opacity = easeFade(1 - (window.scrollY / ( window.innerHeight / 20)));
elementRef.current.style.opacity = `${opacity}`;
};
handleScroll();
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [elementRef]);
return <div style={{opacity: 1}} ref={elementRef} {...props}/>
};

View File

@ -0,0 +1,3 @@
import { ScrollFadingTitle } from "./ScrollFadingTitle";
export default ScrollFadingTitle;

View File

@ -4,43 +4,32 @@ import { cn } from "@/lib/utils";
import { ComponentProps, useEffect, useState } from "react";
const easeSlide = (x: number) => (
x
1 - Math.pow(1 - x, 3)
);
export const SidebarTriggerAdjustable = (props: ComponentProps<"div">) => {
const [scrollY, setScrollY] = useState(0);
const [windowHeight, setWindowHeight] = useState(1);
const isMobile = useIsMobile();
let ticking = false;
useEffect(() => {
const triggerElement = document.querySelector('div[data-sidebar-trigger="true"]');
const handleScroll = () => {
if (!ticking) {
window.requestAnimationFrame(() => {
setScrollY(window.scrollY);
ticking = false;
});
if (!triggerElement) {
console.log("triggerElement is null");
return;
}
const triggerDiv = triggerElement as HTMLDivElement;
const slideAmount = easeSlide(Math.max(0, Math.min(1, window.scrollY / (window.innerHeight / 20))));
triggerDiv.style.marginLeft = `calc(var(--spacing) * ${12 * slideAmount})`;
triggerDiv.style.paddingTop = `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`;
};
const handleResize = () => setWindowHeight(window.innerHeight);
setWindowHeight(window.innerHeight);
window.addEventListener('scroll', handleScroll);
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('scroll', handleScroll);
window.removeEventListener('resize', handleResize);
};
}, []);
}, [isMobile]);
let slideAmount = Math.max(0, Math.min(1, scrollY / (windowHeight / 20)));
slideAmount = easeSlide(slideAmount);
return <div {...props} style={{
marginLeft: `calc(var(--spacing) * ${12 * slideAmount})`,
marginTop: `calc(var(--spacing) * ${(isMobile ? 1 : 3) * slideAmount})`
}}></div>;
return <div data-sidebar-trigger="true" {...props}></div>;
}