Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update sticky header scroll behavior #236

Merged
merged 6 commits into from
Oct 6, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 114 additions & 98 deletions packages/frontendmu-nuxt/components/site/Menu.vue
Original file line number Diff line number Diff line change
@@ -1,161 +1,181 @@
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import { onMounted } from "vue";
Saamiyah marked this conversation as resolved.
Show resolved Hide resolved

const currentPath = computed(() => useRoute().path)

const router = useRouter()
const router = useRouter();

interface TMenuItem {
title: string
href: string
class?: string
children?: TMenuItem[]
title: string;
href: string;
class?: string;
children?: TMenuItem[];
}

interface TMenu {
[key: string]: TMenuItem
[key: string]: TMenuItem;
}

const links: TMenu = {
about: {
title: 'About',
href: '/about',
class: 'hidden md:block',
title: "About",
href: "/about",
class: "hidden md:block",
children: [
// {
// title: 'FAQ',
// href: '/faq',
// class: '',
// },
{
title: 'History',
href: '/history',
class: '',
title: "History",
href: "/history",
class: "",
},
{
title: 'Contribute',
href: '/contribute',
class: '',
title: "Contribute",
href: "/contribute",
class: "",
},
{
title: 'Code of conduct',
href: '/code_of_conduct',
class: '',
title: "Code of conduct",
href: "/code_of_conduct",
class: "",
},
{
title: 'Coding Guidelines',
href: '/coding_guidelines',
class: '',
title: "Coding Guidelines",
href: "/coding_guidelines",
class: "",
},
{
title: 'WhatsApp',
href: 'https://chat.whatsapp.com/invite/0kQ2QX0ZQ0j1YQ4X6Q4Q4Q',
class: '',
title: "WhatsApp",
href: "https://chat.whatsapp.com/invite/0kQ2QX0ZQ0j1YQ4X6Q4Q4Q",
class: "",
},
{
title: 'Instagram',
href: 'https://www.instagram.com/frontend.mu/?ref=frontend.mu',
class: '',
title: "Instagram",
href: "https://www.instagram.com/frontend.mu/?ref=frontend.mu",
class: "",
},
{
title: 'LinkedIn',
href: 'https://www.linkedin.com/company/81846464/admin/?ref=frontend.mu',
class: '',
title: "LinkedIn",
href: "https://www.linkedin.com/company/81846464/admin/?ref=frontend.mu",
class: "",
},
{
title: 'Join Discord',
href: 'https://discord.gg/WxXW9Jvv6k?ref=frontend.mu',
class: '',
title: "Join Discord",
href: "https://discord.gg/WxXW9Jvv6k?ref=frontend.mu",
class: "",
},
{
title: 'GitHub',
href: 'https://github.com/Front-End-Coders-Mauritius?ref=frontend.mu',
class: '',
title: "GitHub",
href: "https://github.com/Front-End-Coders-Mauritius?ref=frontend.mu",
class: "",
},
{
title: 'Twitter',
href: 'https://twitter.com/frontendmu?ref=frontend.mu',
class: '',
title: "Twitter",
href: "https://twitter.com/frontendmu?ref=frontend.mu",
class: "",
},
],
},
meetups: {
title: 'Meetups',
href: '/meetups',
title: "Meetups",
href: "/meetups",
},
community: {
title: 'Community',
href: '/community',
title: "Community",
href: "/community",
},
team: {
title: 'Team',
href: '/team',
class: 'hidden md:block',
title: "Team",
href: "/team",
class: "hidden md:block",
},
sponsors: {
title: 'Sponsors',
href: '/sponsors',
class: 'hidden md:block',
title: "Sponsors",
href: "/sponsors",
class: "hidden md:block",
},
}

function makeHeaderSticky() {
const header = document.querySelector('.menu-wrapper')
if (!header)
return

function handleIntersection(entries: IntersectionObserverEntry[]) {
entries.forEach((entry) => {
if (!header)
return
if (entry.isIntersecting) {
header.classList.remove('intersect')
};

function toggleHeader() {
const $header = document.querySelector(".menu-wrapper");
let currentTransition = 0;
let lastScrollPosition = 0;
if ($header) {
const handleScroll = () => {
const headerHeight = $header.clientHeight;
const currentScrollPosition =
window.scrollY || document.documentElement.scrollTop;
const distance = currentScrollPosition - lastScrollPosition;

const nextTransition = Math.min(
Math.max(currentTransition + distance, 0),
headerHeight
);

if (
currentScrollPosition >= headerHeight &&
nextTransition !== currentTransition
) {
currentTransition = nextTransition;
$header.style.transform = `translateY(-${currentTransition}px)`;
}
else {
header.classList.add('intersect')
}
})
}

const options = {
root: null,
rootMargin: '-200px 0px 0px 0px',
threshold: 0,
}

const observer = new IntersectionObserver(handleIntersection, options)
const target = document.querySelector('#sticky-observer')
if (currentScrollPosition > headerHeight) {
$header.classList.add(
"intersect",
"shadow-sm",
"dark:bg-verse-900/50",
"bg-verse-50/50"
);
} else {
$header.classList.remove(
"intersect",
"shadow-sm",
"dark:bg-verse-900/50",
"bg-verse-50/50"
);
}

if (!target)
return
lastScrollPosition = currentScrollPosition;
};
MrSunshyne marked this conversation as resolved.
Show resolved Hide resolved

observer.observe(target)
window.addEventListener("scroll", handleScroll);
}
MrSunshyne marked this conversation as resolved.
Show resolved Hide resolved
}

function handleRightClick(event: MouseEvent) {
// prevent default and navigate to /branding
event.preventDefault()
router.push('/branding')
event.preventDefault();
router.push("/branding");
}

onMounted(makeHeaderSticky)
onMounted(toggleHeader);
</script>

<template>
<div class="menu-wrapper w-full flex justify-between contain sticky top-0 z-30 h-32 items-center">
<div class="menu-wrapper w-full flex sticky top-0 z-30 h-20 items-center">
<div class="menu theme-light w-full">
<div class="flex justify-between items-center">
<div class="flex justify-between items-center contain">
<div class="flex">
<NuxtLink href="/" class="flex gap-2 text-verse-500 dark:text-verse-200" title="frontend.mu"
@contextmenu="handleRightClick">
<NuxtLink
href="/"
class="flex gap-2 text-verse-500 dark:text-verse-200"
title="frontend.mu"
@contextmenu="handleRightClick"
>
<SiteLogo class="w-10" />
<span class="hidden text-lg font-bold leading-none tracking-tighter md:text-3xl md:block">
<span
class="hidden text-lg font-bold leading-none tracking-tighter md:text-3xl md:block"
>
frontend.mu
</span>
</NuxtLink>
</div>
<nav>
<ul class="nav-links text-sm md:text-sm lg:text-base flex md:gap-4 font-medium font-heading">
<ul
class="nav-links text-sm md:text-sm lg:text-base flex md:gap-4 font-medium font-heading"
>
<template v-for="item of Object.keys(links)" :key="item">
<SiteMenuItem :links="links" :item="item" />
</template>
Expand All @@ -170,7 +190,9 @@ onMounted(makeHeaderSticky)
<slot name="dock-right" />
</div>
</div>
<div class="absolute right-10 top-10 rounded-lg px-4 bg-white/20 shadow-[0px_0px_2px_var(--color-verse-500)]">
<div
class="absolute right-10 top-10 rounded-lg px-4 bg-white/20 shadow-[0px_0px_2px_var(--color-verse-500)]"
>
<slot name="dock-right-bottom" />
</div>
</div>
Expand All @@ -188,13 +210,7 @@ onMounted(makeHeaderSticky)
}

.intersect {
@apply h-16;
}

.intersect .menu {
padding: 4px 7px;
border-radius: 24px;
box-shadow: 0px 0px 2px var(--color-verse-500);
backdrop-filter: brightness(0.8) blur(20px);
backdrop-filter: brightness(1) blur(20px);
}
</style>
Loading