feat: align homepage with youtube layout
This commit is contained in:
1 parent
01b1114909
commit
e29fb4c859
5 files changed
+139
-10
No files matched your search
@@ -1,12 +1,64 @@
|
||||
// Author: fruit.com.cn
|
||||
import { Bell, Bookmark, Compass, History, Home, Menu, Search, UserRound, X } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
|
||||
import { useAppStore } from '../app/store';
|
||||
import { LanguageSwitcher } from '../components/LanguageSwitcher';
|
||||
|
||||
export function AppShell() {
|
||||
const { locale, lang, setLang, query, setQuery, sidebarOpen, setSidebarOpen } = useAppStore(); const navigate = useNavigate();
|
||||
const { locale, query, setQuery, sidebarOpen, setSidebarOpen } = useAppStore();
|
||||
const navigate = useNavigate();
|
||||
const [searchOpen, setSearchOpen] = useState(false);
|
||||
const navItems = [[Home, locale.home, '/'], [Compass, locale.shorts, '/shorts'], [UserRound, locale.subscriptions, '/subscriptions'], [Bookmark, locale.library, '/library'], [History, locale.history, '/history']];
|
||||
const submitSearch = (event) => { event.preventDefault(); navigate(`/search?q=${encodeURIComponent(query)}`); };
|
||||
return <><header className="topbar"><button className="mobile-only icon-button" onClick={() => setSidebarOpen(!sidebarOpen)} aria-label="Menu"><Menu /></button><NavLink className="brand" to="/"><img src="/brand/ymb-logo-blue.png" alt="YMB" /></NavLink><form className="search-form" onSubmit={submitSearch}><input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={locale.search} aria-label={locale.search} /><button aria-label="Search"><Search size={19} /></button></form><div className="top-actions"><LanguageSwitcher /><Bell size={19} /><span className="profile-chip">N</span></div></header><aside className={`sidebar ${sidebarOpen ? 'open' : ''}`}><nav>{navItems.map(([Icon, label, to]) => <NavLink to={to} end={to === '/'} key={to}><Icon size={19} />{label}</NavLink>)}</nav><div className="nav-rule" /><small>GENRE POPULER</small>{locale.genres.slice(1).map((genre) => <button className="genre-link" key={genre} onClick={() => navigate(`/search?genre=${encodeURIComponent(genre)}`)}>{genre}</button>)}</aside><main className="content"><Outlet /></main><nav className="bottom-nav">{navItems.slice(0, 4).map(([Icon, label, to]) => <NavLink to={to} key={to}><Icon size={19} />{label}</NavLink>)}</nav>{sidebarOpen && <button className="scrim mobile-only" onClick={() => setSidebarOpen(false)} aria-label="Close"><X /></button>}</>;
|
||||
const submitSearch = (event) => {
|
||||
event.preventDefault();
|
||||
navigate(`/search?q=${encodeURIComponent(query)}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="topbar">
|
||||
<button className="icon-button topbar-menu" onClick={() => setSidebarOpen(!sidebarOpen)} aria-label="Menu" type="button">
|
||||
<Menu size={22} />
|
||||
</button>
|
||||
<NavLink className="brand" to="/" aria-label="YMB">
|
||||
<img src="/brand/ymb-logo-blue.png" alt="YMB" />
|
||||
</NavLink>
|
||||
<form className={`search-form ${searchOpen ? 'open' : ''}`} onSubmit={submitSearch}>
|
||||
<input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={locale.search} aria-label={locale.search} />
|
||||
<button aria-label="Search" type="submit"><Search size={19} /></button>
|
||||
</form>
|
||||
<div className="top-actions">
|
||||
<button className="icon-button mobile-search" onClick={() => setSearchOpen(!searchOpen)} aria-label={locale.search} type="button"><Search size={20} /></button>
|
||||
<LanguageSwitcher />
|
||||
<Bell size={19} aria-hidden="true" />
|
||||
<span className="profile-chip" aria-label="Profile">N</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<aside className={`sidebar ${sidebarOpen ? 'open' : 'closed'}`}>
|
||||
<nav>
|
||||
{navItems.map(([Icon, label, to]) => (
|
||||
<NavLink to={to} end={to === '/'} key={to}>
|
||||
<Icon size={19} />
|
||||
<span className="sidebar-label">{label}</span>
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
<div className="nav-rule" />
|
||||
<small>GENRE POPULER</small>
|
||||
{locale.genres.slice(1).map((genre) => (
|
||||
<button className="genre-link" key={genre} onClick={() => navigate(`/search?genre=${encodeURIComponent(genre)}`)} type="button">
|
||||
<span className="sidebar-label">{genre}</span>
|
||||
</button>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
<main className={`content ${sidebarOpen ? '' : 'content-wide'}`}><Outlet /></main>
|
||||
<nav className="bottom-nav">
|
||||
{navItems.slice(0, 4).map(([Icon, label, to]) => <NavLink to={to} key={to}><Icon size={19} />{label}</NavLink>)}
|
||||
</nav>
|
||||
{sidebarOpen && <button className="scrim mobile-only" onClick={() => setSidebarOpen(false)} aria-label="Close" type="button"><X /></button>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user