feat(desktop-header): group nav into a centered segmented capsule

Wrap the always-visible nav items in a single rounded track (segmented-control
look): rounded-full border + subtle bg + backdrop blur, centered between the
logo and the right-side actions via mx-auto (fills the previously empty middle).

- All items stay visible with icon + label — no scroll, no shrink, no collapse,
  no hover-reveal. Purely a visual grouping.
- Items become rounded-full pills; the framer-motion active indicator slides
  within the capsule and is lifted above the track (bg + ring + shadow).
- Admin link stays inside the capsule after a thin divider, warning-tinted.
- Right-side actions sit at the edge (dropped ml-auto since nav now centers).
This commit is contained in:
c0mrade
2026-06-05 17:21:21 +03:00
parent 864aeaf600
commit 7de9b3b51b

View File

@@ -147,22 +147,25 @@ export function AppShell({ children }: AppShellProps) {
onClick={handleNavClick} onClick={handleNavClick}
aria-label={label} aria-label={label}
className={cn( className={cn(
'relative flex shrink-0 items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-[13px] font-medium transition-colors duration-200', 'relative flex shrink-0 items-center gap-1.5 rounded-full px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
active active
? admin ? admin
? 'text-warning-400' ? 'text-warning-300'
: 'text-dark-50' : 'text-dark-50'
: admin : admin
? 'text-warning-500/70 hover:bg-warning-500/10 hover:text-warning-400' ? 'text-warning-500/70 hover:bg-warning-500/10 hover:text-warning-300'
: 'text-dark-400 hover:bg-dark-800/50 hover:text-dark-100', : 'text-dark-400 hover:bg-dark-800/60 hover:text-dark-100',
)} )}
> >
{active && ( {active && (
<motion.span <motion.span
layoutId="desktop-nav-active" layoutId="desktop-nav-active"
className={cn( className={cn(
'absolute inset-0 rounded-lg', // Подсветка-пилюля активного пункта — «приподнята» над треком капсулы
admin ? 'bg-warning-500/10' : 'bg-dark-800', 'absolute inset-0 rounded-full shadow-sm',
admin
? 'bg-warning-500/15 ring-1 ring-warning-500/20'
: 'bg-dark-700/80 ring-1 ring-dark-600/40',
)} )}
transition={{ type: 'spring', stiffness: 500, damping: 35 }} transition={{ type: 'spring', stiffness: 500, damping: 35 }}
/> />
@@ -214,19 +217,21 @@ export function AppShell({ children }: AppShellProps) {
<span className="text-base font-semibold text-dark-100">{appName}</span> <span className="text-base font-semibold text-dark-100">{appName}</span>
</Link> </Link>
{/* Navigation — labels always visible, all items shown, no scroll/shrink */} {/* Navigation — единая «капсула» (segmented control): все пункты видны
<nav className="flex items-center gap-0.5"> всегда, без скролла/сжатия/сворачивания. Капсула центрируется в
свободном пространстве между логотипом и кнопками (mx-auto). */}
<nav className="mx-auto flex items-center gap-0.5 rounded-full border border-dark-800/70 bg-dark-900/50 p-1 shadow-sm backdrop-blur-sm">
{desktopNav.map((item) => renderNavLink(item.path, item.label, item.icon))} {desktopNav.map((item) => renderNavLink(item.path, item.label, item.icon))}
{isAdmin && ( {isAdmin && (
<> <>
<div className="mx-1.5 h-5 w-px shrink-0 bg-dark-700/70" /> <div className="mx-1 h-5 w-px shrink-0 bg-dark-700/60" />
{renderNavLink('/admin', t('admin.nav.title'), ShieldIcon, true)} {renderNavLink('/admin', t('admin.nav.title'), ShieldIcon, true)}
</> </>
)} )}
</nav> </nav>
{/* Right side actions — pinned right, never shrink */} {/* Right side actions — pinned right (nav centers via mx-auto), never shrink */}
<div className="ml-auto flex shrink-0 items-center gap-2"> <div className="flex shrink-0 items-center gap-2">
<button <button
onClick={() => { onClick={() => {
haptic.impact('light'); haptic.impact('light');