fixed main page
This commit is contained in:
@@ -1,10 +1,102 @@
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import AppTopBar from "../layout/AppTopBar";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function FeedTopBar({ onMenuClick, typeFilter, onTypeFilterChange }) {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const menuRef = useRef(null);
|
||||
const btnRef = useRef(null);
|
||||
|
||||
const options = [
|
||||
{ key: "loss", label: t("feed.filterLoss") },
|
||||
{ key: null, label: t("feed.filterAll") },
|
||||
{ key: "found", label: t("feed.filterFound") },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const handler = (e) => {
|
||||
if (
|
||||
menuRef.current && !menuRef.current.contains(e.target) &&
|
||||
btnRef.current && !btnRef.current.contains(e.target)
|
||||
) {
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
document.addEventListener("mousedown", handler);
|
||||
return () => document.removeEventListener("mousedown", handler);
|
||||
}, [open]);
|
||||
|
||||
const handleSelect = (key) => {
|
||||
onTypeFilterChange(key);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
export default function FeedTopBar({ onMenuClick }) {
|
||||
return (
|
||||
<div className="f-topbar-dropdown-wrap">
|
||||
<AppTopBar
|
||||
onMenuClick={onMenuClick}
|
||||
hideProfile
|
||||
onMenuClick={onMenuClick}
|
||||
hideProfile
|
||||
hideWordmark
|
||||
centerContent={
|
||||
<button
|
||||
ref={btnRef}
|
||||
className="f-topbar-dropdown-btn"
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
>
|
||||
<span>{t("app.name")}</span>
|
||||
<svg
|
||||
className={`f-topbar-chevron${open ? " open" : ""}`}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
|
||||
<AnimatePresence>
|
||||
{open && (
|
||||
<motion.div
|
||||
className="f-topbar-dropdown-overlay"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.15 }}
|
||||
onClick={() => setOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
<AnimatePresence>
|
||||
{open && (
|
||||
<motion.div
|
||||
ref={menuRef}
|
||||
className="f-topbar-dropdown-menu"
|
||||
initial={{ opacity: 0, scale: 0.95, y: -8 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: -8 }}
|
||||
transition={{ duration: 0.18, ease: "easeOut" }}
|
||||
>
|
||||
{options.map((opt) => (
|
||||
<button
|
||||
key={opt.key ?? "all"}
|
||||
className={`f-topbar-dropdown-item${typeFilter === opt.key ? " active" : ""}`}
|
||||
onClick={() => handleSelect(opt.key)}
|
||||
>
|
||||
{opt.label}
|
||||
</button>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export default function AppTopBar({
|
||||
onLoginClick,
|
||||
loginLabel,
|
||||
hideProfile = false,
|
||||
hideWordmark = false,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@@ -22,7 +23,7 @@ export default function AppTopBar({
|
||||
<button className="f-menu-btn" onClick={onMenuClick}>
|
||||
<MenuIcon />
|
||||
</button>
|
||||
<span className="f-topbar-wordmark">{t('app.name')}</span>
|
||||
{!hideWordmark && <span className="f-topbar-wordmark">{t('app.name')}</span>}
|
||||
<div className="f-topbar-center">{centerContent}</div>
|
||||
{hideProfile ? (
|
||||
<span className="f-topbar-spacer" />
|
||||
|
||||
Reference in New Issue
Block a user