177 lines
6.0 KiB
TypeScript
177 lines
6.0 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
|
||
type TabType = "home" | "courses" | "profile"
|
||
|
||
interface FooterProps {
|
||
onTabChange?: (tab: TabType) => void
|
||
activeTab?: TabType
|
||
}
|
||
|
||
// Строгие и аккуратные SVG иконки
|
||
const HomeIcon = ({ filled = false, className = "" }) => (
|
||
<svg
|
||
className={className}
|
||
fill={filled ? "currentColor" : "none"}
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
strokeWidth={filled ? 0 : 1.5}
|
||
>
|
||
{filled ? (
|
||
<path d="M11.47 3.841a.75.75 0 011.06 0l8.69 8.69a.75.75 0 101.06-1.061l-8.689-8.69a2.25 2.25 0 00-3.182 0l-8.69 8.69a.75.75 0 001.061 1.06l8.69-8.689z" />
|
||
) : (
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
||
/>
|
||
)}
|
||
</svg>
|
||
)
|
||
|
||
const BookIcon = ({ filled = false, className = "" }) => (
|
||
<svg
|
||
className={className}
|
||
fill={filled ? "currentColor" : "none"}
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
strokeWidth={filled ? 0 : 1.5}
|
||
>
|
||
{filled ? (
|
||
<path d="M11.25 4.533A9.707 9.707 0 006 3a9.735 9.735 0 00-3.25.555.75.75 0 00-.5.707v14.25a.75.75 0 001 .707A8.237 8.237 0 016 18.75c1.995 0 3.823.707 5.25 1.886V4.533zM12.75 20.636A8.214 8.214 0 0118 18.75c.966 0 1.89.166 2.75.47a.75.75 0 001-.708V4.262a.75.75 0 00-.5-.707A9.735 9.735 0 0018 3a9.707 9.707 0 00-5.25 1.533v16.103z" />
|
||
) : (
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
d="M12 6.042A9.02 9.02 0 016 3.75c-1.052 0-2.062.18-3 .512v14.25A8.998 8.998 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a9.02 9.02 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.998 8.998 0 0018 18a8.998 8.998 0 00-6 2.292m0-14.25v14.25"
|
||
/>
|
||
)}
|
||
</svg>
|
||
)
|
||
|
||
const UserIcon = ({ filled = false, className = "" }) => (
|
||
<svg
|
||
className={className}
|
||
fill={filled ? "currentColor" : "none"}
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
strokeWidth={filled ? 0 : 1.5}
|
||
>
|
||
{filled ? (
|
||
<path
|
||
fillRule="evenodd"
|
||
d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z"
|
||
clipRule="evenodd"
|
||
/>
|
||
) : (
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
|
||
/>
|
||
)}
|
||
</svg>
|
||
)
|
||
|
||
function Footer({ onTabChange, activeTab: controlledActiveTab }: FooterProps) {
|
||
const [internalActiveTab, setInternalActiveTab] = useState<TabType>("home")
|
||
|
||
const activeTab = controlledActiveTab || internalActiveTab
|
||
|
||
const handleTabChange = (tab: TabType) => {
|
||
if (!controlledActiveTab) {
|
||
setInternalActiveTab(tab)
|
||
}
|
||
onTabChange?.(tab)
|
||
}
|
||
|
||
const tabs = [
|
||
{
|
||
id: "home" as TabType,
|
||
label: "Главная",
|
||
icon: HomeIcon,
|
||
},
|
||
{
|
||
id: "courses" as TabType,
|
||
label: "Курсы",
|
||
icon: BookIcon,
|
||
},
|
||
{
|
||
id: "profile" as TabType,
|
||
label: "Профиль",
|
||
icon: UserIcon,
|
||
},
|
||
]
|
||
|
||
return (
|
||
<div className="fixed bottom-0 left-0 right-0 z-50">
|
||
{/* Основной контейнер */}
|
||
<div className="relative">
|
||
{/* Фон с размытием */}
|
||
<div className="absolute inset-0 bg-slate-900/95 backdrop-blur-2xl border-t border-teal-700/30" />
|
||
|
||
{/* Тонкая верхняя линия */}
|
||
<div className="absolute top-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-teal-500/60 to-transparent" />
|
||
|
||
{/* Контент */}
|
||
<div className="relative px-8 py-4">
|
||
<div className="flex justify-center items-center max-w-sm mx-auto">
|
||
<div className="flex space-x-12">
|
||
{tabs.map((tab) => {
|
||
const isActive = activeTab === tab.id
|
||
const Icon = tab.icon
|
||
|
||
return (
|
||
<button
|
||
key={tab.id}
|
||
onClick={() => handleTabChange(tab.id)}
|
||
className={`
|
||
group relative flex flex-col items-center justify-center
|
||
transition-all duration-200 ease-out
|
||
focus:outline-none focus:ring-2 focus:ring-teal-400/50 focus:ring-offset-2 focus:ring-offset-slate-900
|
||
rounded-lg p-2 min-w-[56px]
|
||
${isActive ? "" : "hover:bg-teal-900/20"}
|
||
`}
|
||
>
|
||
{/* Активный индикатор */}
|
||
{isActive && (
|
||
<div className="absolute -top-1 left-1/2 transform -translate-x-1/2 w-8 h-0.5 bg-gradient-to-r from-teal-400 to-cyan-400 rounded-full" />
|
||
)}
|
||
|
||
{/* Иконка */}
|
||
<div className="mb-1">
|
||
<Icon
|
||
filled={isActive}
|
||
className={`
|
||
w-6 h-6 transition-colors duration-200
|
||
${isActive ? "text-teal-300" : "text-slate-400 group-hover:text-teal-400"}
|
||
`}
|
||
/>
|
||
</div>
|
||
|
||
{/* Подпись */}
|
||
<span
|
||
className={`
|
||
text-xs font-medium transition-colors duration-200
|
||
${isActive ? "text-teal-200" : "text-slate-500 group-hover:text-teal-300"}
|
||
`}
|
||
>
|
||
{tab.label}
|
||
</span>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Нижний отступ для мобильных устройств */}
|
||
<div className="h-2 bg-slate-900/95 sm:h-0" />
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Footer
|