Rehab_React_Vite_my_old/src/components/BottomNavigation.tsx

144 lines
8.3 KiB
TypeScript

"use client"
import type React from "react"
import { useHistory, useLocation } from "react-router-dom"
import { getRouteHome } from "../shared/consts/router"
import { getRouteCourses } from "../shared/consts/router"
import { getRouteCourseExercises } from "../shared/consts/router"
import { getRouteSettings } from "../shared/consts/router"
const BottomNavigation: React.FC = () => {
const history = useHistory()
const location = useLocation()
// Define SVG icons directly within the component, with active state styling
const HomeIcon = ({ active }: { active: boolean; size?: number }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
viewBox="0 0 29 29"
fill={active ? "#2B8794" : "#ffffff"}
stroke={"none"}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m23.487 7.993-6.75-4.723c-1.84-1.29-4.665-1.219-6.435.152L4.43 8.005c-1.172.914-2.098 2.79-2.098 4.266v8.087c0 2.989 2.426 5.426 5.415 5.426h12.634a5.417 5.417 0 0 0 5.415-5.414v-7.947c0-1.582-1.02-3.527-2.309-4.43Zm-8.544 13.103c0 .48-.398.88-.879.88a.885.885 0 0 1-.879-.88V17.58c0-.48.399-.879.88-.879.48 0 .878.399.878.88v3.515Z" />
</svg>
)
const CoursesIcon = ({ active }: { active: boolean; size?: number }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
viewBox="0 0 26 26"
fill={active ? "#2B8794" : "#ffffff"}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M4.147 9.186c.112-1.3.52-2.328 1.21-3.062.69-.733 1.721-1.232 3.194-1.368l.532-.05V2.344a.3.3 0 0 1 .293-.293.3.3 0 0 1 .293.293v2.344h8.79V2.344a.3.3 0 0 1 .293-.293.3.3 0 0 1 .293.293v2.362l.532.05c1.473.136 2.503.635 3.193 1.368.692.734 1.098 1.763 1.21 3.064H4.15a.017.017 0 0 1-.004-.002ZM4.688 12.118H23.44a.59.59 0 0 1 .586.586v7.22c0 1.668-.417 2.969-1.235 3.85-.81.873-2.1 1.425-4.039 1.425H9.376c-1.939 0-3.228-.552-4.039-1.425-.818-.881-1.235-2.182-1.235-3.85v-7.22a.59.59 0 0 1 .586-.586Zm5.945 6.773a1.759 1.759 0 0 0-1.341 0 1.94 1.94 0 0 0-.554.352l-.018.015-.015.017c-.307.324-.5.77-.5 1.235 0 .465.193.911.5 1.235l.015.017.018.016c.164.148.35.266.553.351.201.084.43.139.671.139.242 0 .47-.055.671-.139.204-.085.389-.203.553-.351l.018-.016.015-.017c.307-.324.501-.77.501-1.235 0-.464-.194-.911-.5-1.235l-.016-.017-.018-.015-.127-.106a1.92 1.92 0 0 0-.426-.246Zm0-4.102a1.759 1.759 0 0 0-1.341 0c-.204.085-.39.203-.554.351l-.023.021-.02.023a1.94 1.94 0 0 0-.352.554c-.083.2-.138.43-.138.67 0 .242.055.47.138.67.085.204.204.39.352.554l.02.024.023.02c.164.148.35.267.553.352.201.083.43.138.671.138.242 0 .47-.055.671-.138.204-.085.389-.204.553-.352l.024-.02.02-.024c.148-.164.267-.35.351-.554.084-.2.14-.428.14-.67 0-.24-.056-.47-.14-.67a1.938 1.938 0 0 0-.351-.554l-.02-.022-.024-.022a1.94 1.94 0 0 0-.553-.35Zm4.1 0a1.66 1.66 0 0 0-1.192-.057l-.148.057a1.942 1.942 0 0 0-.553.351l-.017.016-.016.017c-.307.324-.5.771-.5 1.236 0 .464.193.91.5 1.234l.016.017.017.016c.165.148.35.267.553.352.2.083.43.138.671.138.241 0 .47-.055.67-.138.205-.085.39-.204.555-.352l.017-.015.016-.018c.307-.324.5-.77.5-1.234 0-.465-.193-.912-.5-1.236l-.016-.017-.017-.016-.128-.106a1.922 1.922 0 0 0-.409-.237h.001l-.011-.005c-.003 0-.005-.002-.007-.003h-.001Z" />
</svg>
)
const ExerciseIcon = ({ active }: { active: boolean; size?: number }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
viewBox="0 0 29 29"
fill={active ? "#2B8794" : "#ffffff"}
>
<g clipPath="url(#a)">
<path d="M20.136 6.739c.944 0 1.892.183 2.593.716.672.51 1.215 1.417 1.215 3.094v7.03c0 1.677-.543 2.584-1.215 3.095-.701.533-1.65.715-2.593.715-.945 0-1.894-.182-2.595-.715-.672-.51-1.215-1.418-1.215-3.094v-7.031c0-1.677.543-2.584 1.215-3.094.701-.533 1.65-.716 2.595-.716ZM7.994 6.739c.944 0 1.892.183 2.593.716.672.51 1.215 1.417 1.215 3.094v7.03c0 1.677-.543 2.584-1.215 3.095-.701.533-1.65.715-2.593.715-.945 0-1.893-.182-2.595-.715-.671-.51-1.215-1.418-1.215-3.094v-7.031c0-1.677.544-2.584 1.215-3.094.702-.533 1.65-.716 2.595-.716ZM15.154 13.771v.586h-2.18v-.586h2.18Z" />
<path d="M26.37 17.873a.885.885 0 0 1-.879-.879v-5.86c0-.48.399-.879.88-.879.48 0 .878.399.878.88v5.86c0 .48-.398.878-.879.878ZM1.758 17.873a.885.885 0 0 1-.88-.879v-5.86c0-.48.4-.879.88-.879s.879.399.879.88v5.86c0 .48-.399.878-.88.878Z" />
</g>
<defs>
<clipPath id="a">
<path fill="#fff" d="M0,0h28v28h-28z" />
</clipPath>
</defs>
</svg>
)
const SettingsIcon = ({ active }: { active: boolean; size?: number }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={40}
height={40}
viewBox="0 0 28 28"
fill={active ? "#2B8794" : "#ffffff"}
// stroke={active ? "none" : "#ffffff"}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M24.612 19.045H17.58a.885.885 0 0 1-.879-.879c0-.48.399-.879.88-.879h7.031c.48 0 .88.399.88.88 0 .48-.4.878-.88.878ZM24.612 23.733H17.58a.885.885 0 0 1-.879-.879c0-.48.399-.879.88-.879h7.031c.48 0 .88.399.88.88 0 .48-.4.878-.88.878ZM25.784 9.986V4.665c0-1.653-.75-2.321-2.613-2.321h-4.735c-1.864 0-2.614.668-2.614 2.32v5.31c0 1.664.75 2.32 2.614 2.32h4.735c1.863.012 2.613-.656 2.613-2.308ZM12.306 9.986V4.665c0-1.653-.75-2.321-2.613-2.321H4.958c-1.864 0-2.614.668-2.614 2.32v5.31c0 1.664.75 2.32 2.614 2.32h4.735c1.863.012 2.613-.656 2.613-2.308ZM12.306 23.17v-4.734c0-1.864-.75-2.614-2.613-2.614H4.958c-1.864 0-2.614.75-2.614 2.614v4.735c0 1.863.75 2.613 2.614 2.613h4.735c1.863 0 2.613-.75 2.613-2.613Z" />
</svg>
)
const navItems = [
{ path: getRouteHome(), icon: HomeIcon, label: "Домой" },
{ path: getRouteCourses(), icon: CoursesIcon, label: "Курсы" },
{ path: getRouteCourseExercises(':id'), icon: ExerciseIcon, label: "Тренировка" },
{ path: getRouteSettings(), icon: SettingsIcon, label: "Меню" },
]
const isActive = (path: string) => {
// Проверка на совпадение или включение
return location.pathname === path || location.pathname.startsWith(path);
}
return (
<div className="fixed bottom-0 left-0 right-0 bg-gradient-to-br from-[#3ABBC7] to-[#0D212C] px-4 pt-4 pb-8 z-50 shadow-lg rounded-t-3xl">
<nav className="flex justify-around items-center max-w-md mx-auto">
{navItems.map((item) => {
const active = isActive(item.path)
const IconComponent = item.icon
return (
<button
key={item.path}
onClick={() => history.push(item.path)}
className="relative flex flex-col items-center justify-center w-24 h-24 overflow-hidden group focus:outline-none focus-visible:ring-1 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#0D212C]"
>
{/* Active state background (glassmorphism rectangle) */}
<div
className={`absolute rounded-2xl shadow-md transition-all duration-300 ease-out bg-white/60 backdrop-blur-md border border-white/20 ${active ? "opacity-100 scale-100 w-20 h-20 top-2" : "opacity-0 scale-0 w-0 h-0 top-0"
}`}
/>
{/* Icon and Label container */}
<div
className={`relative z-10 flex flex-col items-center justify-center transition-all duration-300 ease-out ${active ? "text-[#145058]" : "text-white/70 translate-y-0 group-hover:text-white"
}`}
>
<div className="mb-1">
<IconComponent active={active} size={24} />
</div>
<span className="text-xs font-medium">{item.label}</span>
<span className="sr-only">{item.label}</span>
</div>
{/* Bottom circle with glow */}
<div
className={`absolute w-4 h-4 rounded-full border-1 border-cyan-900 shadow-lg shadow-[#2BACBE]/50 animate-pulse transition-all duration-300 ease-out ${active ? "opacity-100 scale-100 translate-y-[40px]" : "opacity-0 scale-0"
}`}
>
{/* Внутренний маленький круг */}
<div className="w-3 h-3 bg-cyan-900 rounded-full mt-[1px] ml-[1px]" />
</div>
</button>
)
})}
</nav>
</div>
)
}
export default BottomNavigation