feat: port SiteFooter (liên hệ Như Quỳnh) + BackToTopButton
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ArrowUp } from "lucide-react";
|
||||||
|
import { CSSProperties, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function BackToTopButton() {
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const [scrollProgress, setScrollProgress] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let frameId: number | null = null;
|
||||||
|
|
||||||
|
const updateScrollState = () => {
|
||||||
|
const scrollTop = window.scrollY;
|
||||||
|
const scrollableHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||||
|
const nextProgress = scrollableHeight > 0 ? Math.min(100, Math.max(0, (scrollTop / scrollableHeight) * 100)) : 0;
|
||||||
|
|
||||||
|
setIsVisible(scrollTop > 120);
|
||||||
|
setScrollProgress(nextProgress);
|
||||||
|
frameId = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestUpdate = () => {
|
||||||
|
if (frameId === null) {
|
||||||
|
frameId = window.requestAnimationFrame(updateScrollState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
requestUpdate();
|
||||||
|
window.addEventListener("scroll", requestUpdate, { passive: true });
|
||||||
|
window.addEventListener("resize", requestUpdate);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("scroll", requestUpdate);
|
||||||
|
window.removeEventListener("resize", requestUpdate);
|
||||||
|
if (frameId !== null) {
|
||||||
|
window.cancelAnimationFrame(frameId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: prefersReducedMotion ? "auto" : "smooth",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label="Lên đầu trang"
|
||||||
|
aria-hidden={!isVisible}
|
||||||
|
onClick={handleClick}
|
||||||
|
className="public-back-to-top"
|
||||||
|
data-visible={isVisible ? "true" : "false"}
|
||||||
|
tabIndex={isVisible ? 0 : -1}
|
||||||
|
style={{ "--scroll-progress": `${scrollProgress}%` } as CSSProperties}
|
||||||
|
>
|
||||||
|
<span className="public-back-to-top-ring" aria-hidden="true" />
|
||||||
|
<span className="public-back-to-top-core">
|
||||||
|
<ArrowUp className="h-5 w-5" strokeWidth={2.2} />
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { Globe2, Mail, MapPin } from "lucide-react";
|
||||||
|
import { navigation } from "@/app/content";
|
||||||
|
|
||||||
|
export function SiteFooter() {
|
||||||
|
return (
|
||||||
|
<footer id="contact" className="px-4 pb-8 sm:px-6">
|
||||||
|
<div className="mx-auto grid max-w-7xl gap-8 border-t border-[var(--surface-border)] pt-8 lg:grid-cols-[1.1fr_0.72fr_0.82fr]">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<p className="text-lg font-bold text-[var(--tour-ink)]">VR360 Như Quỳnh</p>
|
||||||
|
<p className="mt-3 max-w-[62ch] text-sm leading-6 text-[var(--muted-foreground)]">
|
||||||
|
Cổng số hóa di tích và không gian văn hóa Xã Như Quỳnh, tỉnh Hưng Yên bằng công nghệ VR360.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav className="grid gap-2 text-sm" aria-label="Liên kết nhanh">
|
||||||
|
<p className="font-bold text-[var(--tour-ink)]">Liên kết nhanh</p>
|
||||||
|
{navigation.map((item) => (
|
||||||
|
<Link key={item.href} href={item.href} className="w-fit text-[var(--muted-foreground)] transition-colors hover:text-[var(--tour-ink)]">
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div className="grid gap-3 text-sm leading-6 text-[var(--muted-foreground)] lg:text-right">
|
||||||
|
<p className="font-bold text-[var(--tour-ink)]">Thông tin liên hệ</p>
|
||||||
|
<p className="inline-flex items-center gap-2 lg:justify-end">
|
||||||
|
<MapPin className="h-4 w-4 text-[var(--primary)]" strokeWidth={1.8} />
|
||||||
|
Xã Như Quỳnh, tỉnh Hưng Yên
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="inline-flex items-center gap-2 font-semibold text-[var(--tour-ink)] hover:text-[var(--primary)] lg:justify-end"
|
||||||
|
>
|
||||||
|
<Globe2 className="h-4 w-4 text-[var(--primary)]" strokeWidth={1.8} />
|
||||||
|
Cổng thông tin Xã Như Quỳnh
|
||||||
|
</a>
|
||||||
|
<Link href="/lien-he" className="inline-flex items-center gap-2 font-semibold text-[var(--tour-ink)] hover:text-[var(--primary)] lg:justify-end">
|
||||||
|
<Mail className="h-4 w-4 text-[var(--primary)]" strokeWidth={1.8} />
|
||||||
|
Gửi góp ý hoặc tư liệu
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto mt-8 max-w-7xl border-t border-[var(--surface-border)] pt-5 text-center text-sm font-semibold text-[var(--muted-foreground)]">
|
||||||
|
© 2026 VR360 Di sản Xã Như Quỳnh.
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user