a tool for shared writing and social publishing

sticky header for tabs in profile page

+33 -25
+2 -2
app/p/[didOrHandle]/(profile)/ProfileHeader.tsx
··· 15 15 let profileRecord = props.profile.record as AppBskyActorProfile.Record; 16 16 17 17 return ( 18 - <> 18 + <div className="flex flex-col" id="profile-header"> 19 19 <Avatar 20 20 src={ 21 21 profileRecord.avatar?.ref && ··· 51 51 </div> 52 52 </div> 53 53 </div> 54 - </> 54 + </div> 55 55 ); 56 56 }; 57 57
+27 -22
app/p/[didOrHandle]/(profile)/ProfileTabs.tsx
··· 11 11 const cardBorderHidden = useCardBorderHidden(); 12 12 const segment = useSelectedLayoutSegment(); 13 13 const currentTab = (segment || "posts") as ProfileTabType; 14 - const [scrollPos, setScrollPos] = useState(0); 14 + const [scrollPosWithinTabContent, setScrollPosWithinTabContent] = useState(0); 15 + const [headerHeight, setHeaderHeight] = useState(0); 15 16 16 17 useEffect(() => { 17 - const profileContent = document.querySelector( 18 - ".overflow-y-scroll", 19 - ) as HTMLElement; 18 + let headerHeight = 19 + document.getElementById("profile-header")?.clientHeight || 0; 20 + setHeaderHeight(headerHeight); 20 21 22 + const profileContent = document.getElementById("profile-content"); 21 23 const handleScroll = () => { 22 24 if (profileContent) { 23 - setScrollPos(profileContent.scrollTop); 25 + setScrollPosWithinTabContent(profileContent.scrollTop - headerHeight); 24 26 } 25 27 }; 26 28 ··· 31 33 }, []); 32 34 33 35 const baseUrl = `/p/${props.didOrHandle}`; 34 - const bgColor = cardBorderHidden ? "var(--bg-leaflet)" : "var(--bg-page)"; 36 + const bgColor = !cardBorderHidden ? "var(--bg-leaflet)" : "var(--bg-page)"; 37 + console.log(scrollPosWithinTabContent); 35 38 36 39 return ( 37 - <div className="flex flex-col w-full sticky top-3 sm:top-4 z-10"> 40 + <div className="flex flex-col w-full sticky top-3 sm:top-4 z-10 sm:px-4 px-3"> 38 41 <div 39 42 style={ 40 - scrollPos < 20 41 - ? { 42 - paddingLeft: `calc(${scrollPos / 20} * 12px + 12px)`, 43 - paddingRight: `calc(${scrollPos / 20} * 12px + 12px)`, 44 - } 45 - : { paddingLeft: "24px", paddingRight: "24px" } 43 + scrollPosWithinTabContent < 0 44 + ? { paddingLeft: "0", paddingRight: "0" } 45 + : scrollPosWithinTabContent > 0 && scrollPosWithinTabContent < 20 46 + ? { 47 + paddingLeft: `calc(${scrollPosWithinTabContent / 20} * 12px )`, 48 + paddingRight: `calc(${scrollPosWithinTabContent / 20} * 12px )`, 49 + } 50 + : { paddingLeft: "12px", paddingRight: "12px" } 46 51 } 47 52 > 48 53 <div 49 54 className={` 50 55 border rounded-lg 51 - ${scrollPos > 20 ? "border-border-light" : "border-transparent"} 56 + ${scrollPosWithinTabContent > 20 ? "border-border-light" : "border-transparent"} 52 57 py-1 53 58 w-full `} 54 59 style={ 55 - scrollPos < 20 60 + scrollPosWithinTabContent < 20 56 61 ? { 57 - backgroundColor: cardBorderHidden 58 - ? `rgba(${bgColor}, ${scrollPos / 60 + 0.75})` 59 - : `rgba(${bgColor}, ${scrollPos / 20})`, 60 - paddingLeft: cardBorderHidden 62 + backgroundColor: !cardBorderHidden 63 + ? `rgba(${bgColor}, ${scrollPosWithinTabContent / 60 + 0.75})` 64 + : `rgba(${bgColor}, ${scrollPosWithinTabContent / 20})`, 65 + paddingLeft: !cardBorderHidden 61 66 ? "4px" 62 - : `calc(${scrollPos / 20} * 4px)`, 63 - paddingRight: cardBorderHidden 67 + : `calc(${scrollPosWithinTabContent / 20} * 4px)`, 68 + paddingRight: !cardBorderHidden 64 69 ? "4px" 65 - : `calc(${scrollPos / 20} * 4px)`, 70 + : `calc(${scrollPosWithinTabContent / 20} * 4px)`, 66 71 } 67 72 : { 68 73 backgroundColor: `rgb(${bgColor})`,
+4 -1
app/p/[didOrHandle]/(profile)/layout.tsx
··· 57 57 > 58 58 <ProfileHeader profile={profile} publications={publications || []} /> 59 59 <ProfileTabs didOrHandle={params.didOrHandle} /> 60 - <div className="h-full pt-3 pb-4 px-3 sm:px-4 flex flex-col"> 60 + <div 61 + className="h-full pt-3 pb-4 px-3 sm:px-4 flex flex-col" 62 + id="profileContent" 63 + > 61 64 {props.children} 62 65 </div> 63 66 </div>