hide/show header on delayed mobile scroll, prevent body scroll when sidebar shown

This commit is contained in:
sk1982 2024-04-12 20:52:47 -04:00
parent b3a697caed
commit 42b02fe5bb
3 changed files with 36 additions and 10 deletions

View File

@ -52,8 +52,11 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
const [userDropdownOpen, setUserDropdownOpen] = useState(false); const [userDropdownOpen, setUserDropdownOpen] = useState(false);
const [menuTranslate, setMenuTranslate] = useState<number | null>(null); const [menuTranslate, setMenuTranslate] = useState<number | null>(null);
const [notificationsTranslate, setNotificationsTranslate] = useState<number | null>(null); const [notificationsTranslate, setNotificationsTranslate] = useState<number | null>(null);
const [navbarShowing, setNavbarShowing] = useState(true);
const reloaded = useReloaded(); const reloaded = useReloaded();
const menusOpened = useRef(false); const menusOpened = useRef(false);
const scrollOffset = useRef(0);
const lastScroll = useRef<number | null>(null);
const path = pathname === '/' ? (user?.homepage ?? '/dashboard') : pathname; const path = pathname === '/' ? (user?.homepage ?? '/dashboard') : pathname;
@ -71,8 +74,23 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
router.replace('', { scroll: false }); router.replace('', { scroll: false });
}, [router, reloaded]); }, [router, reloaded]);
useWindowListener('scroll', () => {
if (lastScroll.current === null) lastScroll.current = window.scrollY;
scrollOffset.current += window.scrollY - lastScroll.current;
lastScroll.current = window.scrollY;
if (scrollOffset.current < -100 || window.scrollY < 35)
setNavbarShowing(true);
else if (scrollOffset.current > 100)
setNavbarShowing(false);
});
useWindowListener('scrollend', () => {
scrollOffset.current = 0;
});
const { ref } = useSwipeable({ const { ref } = useSwipeable({
touchEventOptions: { passive: false }, touchEventOptions: { passive: true },
onSwiped: e => { onSwiped: e => {
const speedX = Math.abs(e.vxvy[0]); const speedX = Math.abs(e.vxvy[0]);
const speedY = Math.abs(e.vxvy[1]); const speedY = Math.abs(e.vxvy[1]);
@ -90,6 +108,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
setMenuTranslate(null); setMenuTranslate(null);
setNotificationsTranslate(null); setNotificationsTranslate(null);
if (!isMenuOpen && !isNotificationsOpen)
document.body.classList.remove('touch-none', 'overflow-hidden'); document.body.classList.remove('touch-none', 'overflow-hidden');
}, },
onSwipeStart: e => { onSwipeStart: e => {
@ -109,21 +128,17 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
if ((isMenuOpen && e.dir === 'Left') || (allMenusClosed && e.dir === 'Right' && xPercent <= 0.6)) { if ((isMenuOpen && e.dir === 'Left') || (allMenusClosed && e.dir === 'Right' && xPercent <= 0.6)) {
setMenuTranslate(e.deltaX); setMenuTranslate(e.deltaX);
e.event.preventDefault();
document.body.classList.add('touch-none', 'overflow-hidden'); document.body.classList.add('touch-none', 'overflow-hidden');
} else if ((isNotificationsOpen && e.dir === 'Right' || (allMenusClosed && e.dir === 'Left' && xPercent >= 0.4))) { } else if ((isNotificationsOpen && e.dir === 'Right' || (allMenusClosed && e.dir === 'Left' && xPercent >= 0.4))) {
setNotificationsTranslate(e.deltaX); setNotificationsTranslate(e.deltaX);
e.event.preventDefault();
document.body.classList.add('touch-none', 'overflow-hidden'); document.body.classList.add('touch-none', 'overflow-hidden');
} }
}, },
onSwiping: e => { onSwiping: e => {
if (menuTranslate !== null) { if (menuTranslate !== null) {
setMenuTranslate(e.deltaX); setMenuTranslate(e.deltaX);
e.event.preventDefault();
} else if (notificationsTranslate !== null) { } else if (notificationsTranslate !== null) {
setNotificationsTranslate(e.deltaX); setNotificationsTranslate(e.deltaX);
e.event.preventDefault();
} }
} }
}) as { ref: RefCallback<Document>; }; }) as { ref: RefCallback<Document>; };
@ -133,6 +148,13 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
return () => ref({} as any); return () => ref({} as any);
}, [ref]); }, [ref]);
useEffect(() => {
if (isMenuOpen || isNotificationsOpen)
document.body.classList.add('touch-none', 'overflow-hidden');
else
document.body.classList.remove('touch-none', 'overflow-hidden');
}, [isMenuOpen, isNotificationsOpen])
useEffect(() => { useEffect(() => {
if (user) if (user)
getFriendRequests().then(setFriendRequests); getFriendRequests().then(setFriendRequests);
@ -236,7 +258,8 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
}, [friendRequests]); }, [friendRequests]);
const topNavbar = useMemo(() => { const topNavbar = useMemo(() => {
return (<Navbar className="w-full fixed" classNames={{ wrapper: 'max-w-full p-0' }} shouldHideOnScroll={breakpoint === undefined} height="5.5rem"> return (<Navbar className={`w-lvw fixed transition ${!navbarShowing && breakpoint === undefined ? '-translate-y-full' : ''}`}
classNames={{ wrapper: 'max-w-full p-0' }} height="5.5rem">
<div className="flex h-header px-6 items-center flex-shrink-0 w-full z-[48]"> <div className="flex h-header px-6 items-center flex-shrink-0 w-full z-[48]">
<Button className="text-2xl font-bold cursor-pointer flex items-center m-0 ps-1.5 pe-2 mr-6" variant="light" <Button className="text-2xl font-bold cursor-pointer flex items-center m-0 ps-1.5 pe-2 mr-6" variant="light"
onClick={() => setMenuOpen(true)}> onClick={() => setMenuOpen(true)}>
@ -323,7 +346,7 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
} }
</div> </div>
</Navbar>); </Navbar>);
}, [breakpoint, routeGroup, friendRequests, userDropdownOpen, isNotificationsOpen, user, notifications]); }, [breakpoint, routeGroup, friendRequests, userDropdownOpen, isNotificationsOpen, user, notifications, navbarShowing]);
const leftSidebar = useMemo(() => { const leftSidebar = useMemo(() => {
return (<div className={`fixed inset-0 w-full h-full z-[49] ${isMenuOpen ? '' : 'pointer-events-none'}`}> return (<div className={`fixed inset-0 w-full h-full z-[49] ${isMenuOpen ? '' : 'pointer-events-none'}`}>
@ -472,10 +495,10 @@ export const HeaderSidebar = ({ children }: HeaderSidebarProps) => {
{ leftSidebar } { leftSidebar }
{ rightSidebar } { rightSidebar }
{/* begin top navbar */} {/* begin top navbar */}
<div className="flex flex-col flex-grow"> <div className="flex flex-col flex-grow h-full">
{topNavbar} {topNavbar}
<div className={`sm:px-5 flex-grow pt-fixed flex flex-col`}> <div className={`sm:px-5 flex-grow pt-fixed flex flex-col h-full`}>
{children} {children}
</div> </div>
</div> </div>

View File

@ -16,6 +16,10 @@ $scrollbar-size: 13px;
// dont theme scrollbars on mobile // dont theme scrollbars on mobile
@media (hover: hover) { @media (hover: hover) {
html {
scrollbar-gutter: stable;
}
@supports not selector(::-webkit-scrollbar) { @supports not selector(::-webkit-scrollbar) {
* { * {
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-bg); scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-bg);

View File

@ -492,7 +492,6 @@ const payloadValid = (payload: any, filterers: Filterers<any, any>) => {
for (const filterer of filterers) { for (const filterer of filterers) {
if (!(filterer.name in payload)) continue; if (!(filterer.name in payload)) continue;
const data = payload[filterer.name]; const data = payload[filterer.name];
console.log(filterer.name, data)
if (filterer.type === 'select' && !(data instanceof Set)) if (filterer.type === 'select' && !(data instanceof Set))
return false; return false;
if (filterer.type === 'slider' && !Array.isArray(data)) if (filterer.type === 'slider' && !Array.isArray(data))