Files
RRS/src/components/Layout.jsx
T

219 lines
6.7 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useState } from "react";
import { Link, NavLink, Outlet, useLocation } from "react-router-dom";
import { company, navigation, phoneHref } from "../data";
import { Arrow, WhatsAppAction } from "./Primitives";
import "./Layout.css";
function Brand() {
return (
<Link className="brand" to="/" aria-label="RRS home">
<img
className="brand__logo"
src="/images/RRSLOGO.png"
alt="Radhe Radhe Synthetics"
width="1024"
height="809"
/>
<span className="brand__name">{company.shortName}</span>
</Link>
);
}
function Header() {
const [menuOpen, setMenuOpen] = useState(false);
const closeMenu = () => setMenuOpen(false);
return (
<header
className="site-header"
onKeyDown={(event) => {
if (menuOpen && event.key === "Escape") {
closeMenu();
document.getElementById("menu-toggle")?.focus();
}
}}
>
<div className="contact-strip">
<span>Contact RRS</span>
<div className="contact-strip__details">
<a href={`mailto:${company.email}`}>{company.email}</a>
<span aria-hidden="true">·</span>
<a href={phoneHref(company.phone)}>{company.phone}</a>
</div>
</div>
<div className="site-header__inner">
<Brand />
<button
id="menu-toggle"
className="site-header__toggle"
type="button"
aria-controls="primary-navigation"
aria-expanded={menuOpen}
onClick={() => setMenuOpen((open) => !open)}
>
{menuOpen ? "Close" : "Menu"}
<span aria-hidden="true">{menuOpen ? "−" : "+"}</span>
</button>
<nav
id="primary-navigation"
aria-label="Main navigation"
className={`site-nav${menuOpen ? " site-nav--open" : ""}`}
>
{navigation.map(({ to, label }) => (
<NavLink
key={to}
to={to}
end={to === "/"}
onClick={closeMenu}
className={({ isActive }) =>
`site-nav__link${isActive ? " site-nav__link--active" : ""}`
}
>
{label}
</NavLink>
))}
<Link
className="site-nav__contact"
to="/contact#whatsapp"
onClick={closeMenu}
>
Text us on WhatsApp <Arrow diagonal />
</Link>
</nav>
</div>
</header>
);
}
function Footer() {
return (
<footer className="site-footer">
<div className="site-footer__top">
<div className="site-footer__intro">
<p className="site-footer__name">{company.name}</p>
<p className="site-footer__description">{company.description}</p>
</div>
<nav aria-label="Footer navigation" className="site-footer__nav">
<span className="site-footer__label">Explore</span>
{navigation.map(({ to, label }) => (
<Link className="site-footer__link" to={to} key={to}>
{label}
</Link>
))}
</nav>
<div className="site-footer__contact">
<span className="site-footer__label">Commercial enquiries</span>
<Link className="site-footer__link" to="/contact#whatsapp">
WhatsApp <Arrow diagonal />
</Link>
<Link className="site-footer__link" to="/contact#direct-contact">
Phone · Email · Address <Arrow diagonal />
</Link>
</div>
</div>
<div className="site-footer__bottom">
<span className="site-footer__wordmark" aria-hidden="true">
RRS.
</span>
<p className="site-footer__signature">
Built to develop.
<br />
<em>Built to repeat.</em>
</p>
</div>
<div className="site-footer__legal">
<span>
© {new Date().getFullYear()} Radhe Radhe Synthetics Pvt. Ltd.
</span>
<span>Commercial supply: grey fabric only.</span>
<a className="site-footer__top-link" href="#top">
Back to top ↑
</a>
</div>
</footer>
);
}
export function ContactBanner({
title = (
<>
Developing your
<br />
<em>next construction?</em>
</>
),
text = "Talk to us about fabric structure, yarn combinations, bulk requirements or new developments.",
}) {
return (
<section className="contact-banner">
<div className="contact-banner__copy">
<p className="contact-banner__label">
A conversation is where it begins
</p>
<h2 className="contact-banner__title">{title}</h2>
<p className="contact-banner__text">{text}</p>
<WhatsAppAction variant="light" />
</div>
<div className="contact-banner__motif" aria-hidden="true">
<span className="contact-banner__loop" />
<span className="contact-banner__loop" />
<span className="contact-banner__loop" />
</div>
</section>
);
}
export default function Layout() {
const { pathname, hash, key } = useLocation();
useEffect(() => {
const title =
navigation.find(({ to }) => to === pathname)?.label ?? "Page not found";
document.title = `${title === "Home" ? "RRS" : `${title} - RRS`} | Radhe Radhe Synthetics`;
const target = hash
? document.getElementById(hash.slice(1))
: document.getElementById("main-content");
if (hash && target) target.scrollIntoView({ behavior: "instant" });
else window.scrollTo({ top: 0, behavior: "instant" });
target?.focus({ preventScroll: true });
}, [pathname, hash, key]);
useEffect(() => {
const sections = [...document.querySelectorAll("#main-content > section")];
const reducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
if (reducedMotion || !("IntersectionObserver" in window)) {
sections.forEach((section) => section.classList.add("reveal--visible"));
return undefined;
}
sections.forEach((section) => section.classList.add("reveal"));
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add("reveal--visible");
observer.unobserve(entry.target);
});
},
{ threshold: 0.08, rootMargin: "0px 0px -8% 0px" },
);
sections.forEach((section) => observer.observe(section));
return () => observer.disconnect();
}, [pathname, key]);
return (
<div className="site-shell" id="top">
<a className="skip-link" href="#main-content">
Skip to content
</a>
<Header key={key} />
<main className="site-main" id="main-content" tabIndex={-1}>
<Outlet />
</main>
<Footer />
</div>
);
}