Implement auto-hiding sticky navbar on scroll and pointer-event safe smooth scroll
This commit is contained in:
+60
-15
@@ -380,6 +380,30 @@ export default function App({ previewMode = false }) {
|
|||||||
const [isCareersOpen, setIsCareersOpen] = useState(false);
|
const [isCareersOpen, setIsCareersOpen] = useState(false);
|
||||||
const [activeSection, setActiveSection] = useState('home');
|
const [activeSection, setActiveSection] = useState('home');
|
||||||
const [currentPage, setCurrentPage] = useState('home');
|
const [currentPage, setCurrentPage] = useState('home');
|
||||||
|
const [navbarVisible, setNavbarVisible] = useState(true);
|
||||||
|
const lastScrollY = useRef(0);
|
||||||
|
|
||||||
|
const smoothScrollTo = (top) => {
|
||||||
|
document.body.style.pointerEvents = 'none';
|
||||||
|
window.scrollTo({ top, behavior: 'smooth' });
|
||||||
|
|
||||||
|
let scrollTimeout;
|
||||||
|
const handleScrollEnd = () => {
|
||||||
|
document.body.style.pointerEvents = '';
|
||||||
|
window.removeEventListener('scrollend', handleScrollEnd);
|
||||||
|
window.removeEventListener('scroll', handleScrollDebounce);
|
||||||
|
clearTimeout(scrollTimeout);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScrollDebounce = () => {
|
||||||
|
clearTimeout(scrollTimeout);
|
||||||
|
scrollTimeout = setTimeout(handleScrollEnd, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scrollend', handleScrollEnd);
|
||||||
|
window.addEventListener('scroll', handleScrollDebounce);
|
||||||
|
scrollTimeout = setTimeout(handleScrollEnd, 800);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const syncPageFromHash = () => {
|
const syncPageFromHash = () => {
|
||||||
@@ -387,11 +411,11 @@ export default function App({ previewMode = false }) {
|
|||||||
if (hash === '#contact' || hash === '#contactus') {
|
if (hash === '#contact' || hash === '#contactus') {
|
||||||
setCurrentPage('contact');
|
setCurrentPage('contact');
|
||||||
setActiveSection('contactus');
|
setActiveSection('contactus');
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
smoothScrollTo(0);
|
||||||
} else if (hash === '#about' || hash === '#aboutus') {
|
} else if (hash === '#about' || hash === '#aboutus') {
|
||||||
setCurrentPage('about');
|
setCurrentPage('about');
|
||||||
setActiveSection('aboutus');
|
setActiveSection('aboutus');
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
smoothScrollTo(0);
|
||||||
} else if (hash === '#home' || hash === '') {
|
} else if (hash === '#home' || hash === '') {
|
||||||
setCurrentPage('home');
|
setCurrentPage('home');
|
||||||
}
|
}
|
||||||
@@ -461,20 +485,36 @@ export default function App({ previewMode = false }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (previewMode || currentPage === 'contact') return;
|
if (previewMode) return;
|
||||||
|
|
||||||
|
let lastScroll = window.scrollY;
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
|
const currentScroll = window.scrollY;
|
||||||
|
const diff = currentScroll - lastScroll;
|
||||||
|
|
||||||
|
if (currentScroll <= 50) {
|
||||||
|
setNavbarVisible(true);
|
||||||
|
} else if (diff > 5 && currentScroll > 100) {
|
||||||
|
setNavbarVisible(false);
|
||||||
|
} else if (diff < -5) {
|
||||||
|
setNavbarVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScroll = currentScroll;
|
||||||
|
|
||||||
|
if (currentPage === 'home') {
|
||||||
// If at top of the page, home is active
|
// If at top of the page, home is active
|
||||||
if (window.scrollY < 100) {
|
if (currentScroll < 100) {
|
||||||
setActiveSection('home');
|
setActiveSection('home');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sections = ['home', 'aboutus', 'services', 'whyus', 'products', 'insights', 'contactus'];
|
const sections = ['home', 'aboutus', 'services', 'whyus', 'products', 'insights', 'contactus'];
|
||||||
const scrollPosition = window.scrollY + 120; // 120px offset for header height and visual center
|
const scrollPosition = currentScroll + 120; // 120px offset for header height and visual center
|
||||||
|
|
||||||
// Special case: if scrolled down near the very bottom of the page, contactus is active
|
// Special case: if scrolled down near the very bottom of the page, contactus is active
|
||||||
if (window.scrollY > 300 && window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 100) {
|
if (currentScroll > 300 && window.innerHeight + currentScroll >= document.documentElement.scrollHeight - 100) {
|
||||||
setActiveSection('contactus');
|
setActiveSection('contactus');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -490,12 +530,13 @@ export default function App({ previewMode = false }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
window.addEventListener('scroll', handleScroll);
|
||||||
handleScroll(); // run once on mount
|
handleScroll(); // run once on mount
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
}, [previewMode]);
|
}, [previewMode, currentPage]);
|
||||||
|
|
||||||
const getActiveNavItem = () => {
|
const getActiveNavItem = () => {
|
||||||
if (currentPage === 'contact') return 'Contact Us';
|
if (currentPage === 'contact') return 'Contact Us';
|
||||||
@@ -548,15 +589,16 @@ export default function App({ previewMode = false }) {
|
|||||||
const el = document.getElementById(sectionId);
|
const el = document.getElementById(sectionId);
|
||||||
if (el) {
|
if (el) {
|
||||||
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
|
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
|
||||||
window.scrollTo({ top, behavior: 'smooth' });
|
smoothScrollTo(top);
|
||||||
setActiveSection(sectionId);
|
setActiveSection(sectionId);
|
||||||
} else if (sectionId === 'contactus') {
|
} else if (sectionId === 'contactus') {
|
||||||
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });
|
smoothScrollTo(document.documentElement.scrollHeight);
|
||||||
setActiveSection('contactus');
|
setActiveSection('contactus');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNavClick = (item) => {
|
const handleNavClick = (item) => {
|
||||||
|
setNavbarVisible(true);
|
||||||
setMobileMenuOpen(false);
|
setMobileMenuOpen(false);
|
||||||
if (item === 'Careers') {
|
if (item === 'Careers') {
|
||||||
setIsCareersOpen(true);
|
setIsCareersOpen(true);
|
||||||
@@ -567,7 +609,7 @@ export default function App({ previewMode = false }) {
|
|||||||
setCurrentPage('contact');
|
setCurrentPage('contact');
|
||||||
setActiveSection('contactus');
|
setActiveSection('contactus');
|
||||||
window.location.hash = 'contact';
|
window.location.hash = 'contact';
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
smoothScrollTo(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,7 +617,7 @@ export default function App({ previewMode = false }) {
|
|||||||
setCurrentPage('about');
|
setCurrentPage('about');
|
||||||
setActiveSection('aboutus');
|
setActiveSection('aboutus');
|
||||||
window.location.hash = 'aboutus';
|
window.location.hash = 'aboutus';
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
smoothScrollTo(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,14 +629,14 @@ export default function App({ previewMode = false }) {
|
|||||||
|
|
||||||
const doScroll = (sectionId) => {
|
const doScroll = (sectionId) => {
|
||||||
if (!sectionId || sectionId === 'home') {
|
if (!sectionId || sectionId === 'home') {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
smoothScrollTo(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const el = document.getElementById(sectionId);
|
const el = document.getElementById(sectionId);
|
||||||
if (el) {
|
if (el) {
|
||||||
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
|
const top = el.getBoundingClientRect().top + window.scrollY - NAV_OFFSET;
|
||||||
window.scrollTo({ top, behavior: 'smooth' });
|
smoothScrollTo(top);
|
||||||
setActiveSection(sectionId);
|
setActiveSection(sectionId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -695,9 +737,12 @@ export default function App({ previewMode = false }) {
|
|||||||
|
|
||||||
{/* Navigation Bar */}
|
{/* Navigation Bar */}
|
||||||
<nav className="glass site-nav" style={{
|
<nav className="glass site-nav" style={{
|
||||||
position: 'sticky', top: 0, zIndex: 50,
|
position: 'sticky',
|
||||||
|
top: navbarVisible ? 0 : (isMobile ? '-72px' : '-88px'),
|
||||||
|
zIndex: 50,
|
||||||
padding: isMobile ? '0.85rem 1.25rem' : '1.2rem 4rem',
|
padding: isMobile ? '0.85rem 1.25rem' : '1.2rem 4rem',
|
||||||
borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)'
|
borderBottom: activeSection === 'products' ? '1px solid rgba(15, 23, 42, 0.08)' : '1px solid rgba(255, 255, 255, 0.05)',
|
||||||
|
transition: 'top 0.3s cubic-bezier(0.16, 1, 0.3, 1), border-bottom 0.3s ease, padding 0.3s ease'
|
||||||
}}>
|
}}>
|
||||||
<div style={{ maxWidth: '1440px', margin: '0 auto', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
<div style={{ maxWidth: '1440px', margin: '0 auto', width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user