initial site
This commit is contained in:
commit
da5b259d06
167 files changed
+34157
No files matched your search
@@ -0,0 +1,178 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Preloader from "./components/Preloader";
|
||||
import AgeGate from "./components/AgeGate";
|
||||
import Header from "./components/Header";
|
||||
import Hero from "./components/Hero";
|
||||
import AtmosphereCarousel from "./components/AtmosphereCarousel";
|
||||
import MenuCarousel from "./components/MenuCarousel";
|
||||
import VipCalculator from "./components/VipCalculator";
|
||||
import Features from "./components/Features";
|
||||
import Contacts from "./components/Contacts";
|
||||
import Footer from "./components/Footer";
|
||||
import FlavorsPage from "./pages/FlavorsPage";
|
||||
import GamesPage from "./pages/GamesPage";
|
||||
import MixesPage from "./pages/MixesPage";
|
||||
import AiMixPage from "./pages/AiMixPage";
|
||||
import BoardGamesPage from "./pages/BoardGamesPage";
|
||||
import MenuPage from "./pages/MenuPage";
|
||||
import TrackOrderPage from "./pages/TrackOrderPage";
|
||||
import { useHashLocation } from "./hooks/useHashLocation";
|
||||
import { isPrerender } from "./lib/prerender";
|
||||
|
||||
type CatalogPage = "flavors" | "games" | "mixes" | "ai-mix" | "board-games" | "menu" | "track" | null;
|
||||
|
||||
function getCatalogPage(hash: string): CatalogPage {
|
||||
if (hash === "#/flavors") return "flavors";
|
||||
if (hash === "#/games") return "games";
|
||||
if (hash === "#/mixes") return "mixes";
|
||||
if (hash === "#/ai-mix" || hash === "#/mix-builder") return "ai-mix";
|
||||
if (hash.startsWith("#mix=")) return "ai-mix"; // share-ссылка на генерацию
|
||||
if (hash === "#/board-games") return "board-games";
|
||||
if (hash === "#/menu" || hash === "#/tea") return "menu";
|
||||
if (hash.startsWith("#/track")) return "track"; // #/track и #/track?code=XXX
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const hash = useHashLocation();
|
||||
const catalogPage = getCatalogPage(hash);
|
||||
const isCatalog = catalogPage !== null;
|
||||
const wasCatalog = useRef(isCatalog);
|
||||
const homeTitle = useRef(document.title);
|
||||
// в пререндер-снапшоте рендерим финальное видимое состояние страницы
|
||||
const prerender = isPrerender();
|
||||
const started = loaded || prerender;
|
||||
|
||||
useEffect(() => {
|
||||
const previousOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
const t = setTimeout(() => {
|
||||
document.body.style.overflow = previousOverflow;
|
||||
setLoaded(true);
|
||||
}, 2450);
|
||||
return () => {
|
||||
clearTimeout(t);
|
||||
document.body.style.overflow = previousOverflow;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (catalogPage === "flavors") {
|
||||
document.title = "Вкусы табака | НЕОН, Нягань";
|
||||
} else if (catalogPage === "games") {
|
||||
document.title = "Игры PS5 | НЕОН, Нягань";
|
||||
} else if (catalogPage === "mixes") {
|
||||
document.title = "Миксы кальянов | НЕОН, Нягань";
|
||||
} else if (catalogPage === "ai-mix") {
|
||||
document.title = "Автоподбор микса | НЕОН, Нягань";
|
||||
} else if (catalogPage === "board-games") {
|
||||
document.title = "Настольные игры | НЕОН, Нягань";
|
||||
} else if (catalogPage === "menu") {
|
||||
document.title = "Меню бара: Авторский чай и Пивная карта | НЕОН, Нягань";
|
||||
} else if (catalogPage === "track") {
|
||||
document.title = "Статус заказа | НЕОН, Нягань";
|
||||
} else {
|
||||
document.title = homeTitle.current;
|
||||
}
|
||||
}, [catalogPage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loaded) return;
|
||||
const returningToHome = wasCatalog.current && !isCatalog;
|
||||
wasCatalog.current = isCatalog;
|
||||
|
||||
if (catalogPage === "flavors") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("flavors-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "games") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("games-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "mixes") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("mixes-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "ai-mix") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("ai-mix-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "board-games") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("board-games-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "menu") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("menu-page-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (catalogPage === "track") {
|
||||
window.scrollTo({ top: 0, behavior: "instant" });
|
||||
document.getElementById("track-title")?.focus({ preventScroll: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// The landing section mounts after the hash changes when leaving the catalog.
|
||||
const section = document.getElementById(hash.slice(1) || "home");
|
||||
if (!section) return;
|
||||
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
section.scrollIntoView({
|
||||
block: "start",
|
||||
behavior: returningToHome || reducedMotion ? "instant" : "smooth",
|
||||
});
|
||||
if (returningToHome) {
|
||||
section.tabIndex = -1;
|
||||
section.focus({ preventScroll: true });
|
||||
}
|
||||
}, [hash, catalogPage, isCatalog, loaded]);
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen w-full max-w-full overflow-x-hidden bg-void font-sans text-mist antialiased">
|
||||
<div className="noise" aria-hidden />
|
||||
{/* в пререндер-снапшоте прелоадер и age-gate не попадают в HTML:
|
||||
бот должен видеть финальную страницу, а не оверлеи */}
|
||||
{!prerender && <Preloader done={loaded} />}
|
||||
{!prerender && <AgeGate ready={loaded} />}
|
||||
<Header started={started} />
|
||||
<main className="w-full max-w-full overflow-x-hidden">
|
||||
{catalogPage === "flavors" ? (
|
||||
<FlavorsPage />
|
||||
) : catalogPage === "games" ? (
|
||||
<GamesPage />
|
||||
) : catalogPage === "mixes" ? (
|
||||
<MixesPage />
|
||||
) : catalogPage === "ai-mix" ? (
|
||||
<AiMixPage />
|
||||
) : catalogPage === "board-games" ? (
|
||||
<BoardGamesPage />
|
||||
) : catalogPage === "menu" ? (
|
||||
<MenuPage />
|
||||
) : catalogPage === "track" ? (
|
||||
<TrackOrderPage />
|
||||
) : (
|
||||
<>
|
||||
<Hero started={started} />
|
||||
<AtmosphereCarousel />
|
||||
<MenuCarousel />
|
||||
<Features />
|
||||
<VipCalculator />
|
||||
<Contacts />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user