diff --git a/public/locales/en.d.ts b/public/locales/en.d.ts
index 8d4f107..f736aed 100644
--- a/public/locales/en.d.ts
+++ b/public/locales/en.d.ts
@@ -6,4 +6,7 @@ export declare const resources: {
"nav.game": "Game";
"nav.cashdesk": "Cashdesk";
"nav.menu": "Menu";
+ "nav.roulette": "Roulette";
+ "nav.tasks": "Tasks";
+ "nav.earnings": "Earnings";
};
diff --git a/public/locales/en.json b/public/locales/en.json
index f5f8395..e16142f 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -5,5 +5,8 @@
"nav.apiary": "Apiary",
"nav.game": "Game",
"nav.cashdesk": "Cashdesk",
- "nav.menu": "Menu"
+ "nav.menu": "Menu",
+ "nav.roulette": "Roulette",
+ "nav.tasks": "Tasks",
+ "nav.earnings": "Earnings"
}
diff --git a/public/locales/ru.d.ts b/public/locales/ru.d.ts
index af8df85..f24b1fa 100644
--- a/public/locales/ru.d.ts
+++ b/public/locales/ru.d.ts
@@ -6,4 +6,7 @@ export declare const resources: {
"nav.game": "Игра";
"nav.cashdesk": "Касса";
"nav.menu": "Меню";
+ "nav.roulette": "Рулетка";
+ "nav.tasks": "Задания";
+ "nav.earnings": "Заработок";
};
diff --git a/public/locales/ru.json b/public/locales/ru.json
index e655116..11d0099 100644
--- a/public/locales/ru.json
+++ b/public/locales/ru.json
@@ -5,5 +5,8 @@
"nav.apiary": "Пасека",
"nav.game": "Игра",
"nav.cashdesk": "Касса",
- "nav.menu": "Меню"
+ "nav.menu": "Меню",
+ "nav.roulette": "Рулетка",
+ "nav.tasks": "Задания",
+ "nav.earnings": "Заработок"
}
diff --git a/src/routes/-/Header/assets/menu.svg b/src/routes/-/Header/assets/menu.svg
new file mode 100644
index 0000000..43cf478
--- /dev/null
+++ b/src/routes/-/Header/assets/menu.svg
@@ -0,0 +1,162 @@
+
diff --git a/src/routes/-/Header/assets/user-bar.svg b/src/routes/-/Header/assets/user-bar.svg
new file mode 100644
index 0000000..c6bb71c
--- /dev/null
+++ b/src/routes/-/Header/assets/user-bar.svg
@@ -0,0 +1,285 @@
+
diff --git a/src/routes/-/Navigation/Navigation.module.css b/src/routes/-/Navigation/Navigation.module.css
index 1ffb666..f383d43 100644
--- a/src/routes/-/Navigation/Navigation.module.css
+++ b/src/routes/-/Navigation/Navigation.module.css
@@ -11,22 +11,14 @@
bottom: 0;
left: 0;
z-index: 10;
+ max-width: 320px;
+ margin: auto;
display: flex;
align-items: flex-end;
justify-content: space-evenly;
overflow: hidden;
}
- .barContainer {
- display: flex;
- align-items: flex-end;
- }
-
- .barWrapper {
- display: flex;
- align-items: flex-end;
- }
-
.bar {
width: 54px;
border-radius: 27px 27px 0 0;
@@ -37,12 +29,6 @@
padding-bottom: var(--navigation-padding);
}
- .barActive {
- }
-
- .barInactive {
- }
-
.safeContent {
display: flex;
flex-direction: column;
@@ -50,23 +36,21 @@
justify-content: center;
width: 100%;
height: 64px;
+ margin-top: -10px;
}
- .iconPlaceholder {
- width: 20px;
- height: 20px;
- background: rgb(255 255 255 / 0.4);
- border-radius: 4px;
- flex-shrink: 0;
- margin-top: -6px;
+ .icon {
+ width: 40px;
+ height: 40px;
margin-bottom: -6px;
}
.label {
- font-size: 11px;
+ font-size: 15px;
+ font-weight: 700;
text-align: center;
-webkit-text-stroke: 1px #331b01;
- text-shadow: 0px 1px 2px 0px #0000008c;
+ text-shadow: 0px 2px 2px #0000008c;
line-height: 1;
}
@@ -77,4 +61,43 @@
.labelActive {
color: #ffbd42;
}
+
+ .menuOverlay {
+ position: absolute;
+ right: 0;
+ bottom: calc(var(--navigation-total) + 16px);
+ z-index: 9;
+ padding-bottom: 8px;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 8px;
+ overflow: hidden;
+ pointer-events: none;
+ }
+
+ .menuOverlay > * {
+ pointer-events: auto;
+ }
+
+ .menuBar {
+ height: 54px;
+ width: 94px;
+ border-radius: 27px 0 0 27px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ padding-right: 20px;
+ }
+
+ .menuBarContent {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 54px;
+ height: 100%;
+ margin-left: -10px;
+ }
}
diff --git a/src/routes/-/Navigation/Navigation.tsx b/src/routes/-/Navigation/Navigation.tsx
index dcd5ccb..6985004 100644
--- a/src/routes/-/Navigation/Navigation.tsx
+++ b/src/routes/-/Navigation/Navigation.tsx
@@ -1,97 +1,198 @@
-import { Link, useMatchRoute } from "@tanstack/react-router";
-import { motion } from "motion/react";
+import { useMatchRoute, useNavigate } from "@tanstack/react-router";
import { useTranslation } from "react-i18next";
+import { AnimatePresence } from "motion/react";
import GlassSurface from "@components/surface/GlassSurface";
import classes from "./Navigation.module.css";
+import ShopRoute from "@/routes/shop";
+import ApiaryRoute from "@/routes/apiary";
+import GameRoute from "@/routes/game";
+import CashdeskRoute from "@/routes/cashdesk";
+import RouletteRoute from "@/routes/roulette";
+import TasksRoute from "@/routes/tasks";
+import EarningsRoute from "@/routes/earnings";
+import { useCallback, useEffect, useRef, useState } from "react";
-const ENTRANCE_DELAYS = [0.2, 0.1, 0, 0.1, 0.2];
+import shopIcon from "./assets/shop.svg";
+import apiaryIcon from "./assets/apiary.svg";
+import gameIcon from "./assets/game.svg";
+import cashdeskIcon from "./assets/cashdesk.svg";
+import menuIcon from "./assets/menu.svg";
+import rouletteIcon from "./assets/roulette.svg";
+import tasksIcon from "./assets/tasks.svg";
+import earningsIcon from "./assets/earnings.svg";
-const SPRING_ENTRANCE = { type: "spring" as const, stiffness: 400, damping: 20 };
-const SPRING_HEIGHT = { type: "spring" as const, stiffness: 500, damping: 25 };
+const BAR_HEIGHT = 64;
+const ACTIVE_BAR_HEIGHT = 74;
+const OFFSCREEN_BAR_OFFSET = 20;
-type NavItem =
- | { key: string; route: string; isMenu?: false }
- | { key: string; route?: undefined; isMenu: true };
-
-const NAV_ITEMS: NavItem[] = [
- { key: "nav.shop", route: "/shop/" },
- { key: "nav.apiary", route: "/apiary/" },
- { key: "nav.game", route: "/game/" },
- { key: "nav.cashdesk", route: "/cashdesk/" },
- { key: "nav.menu", isMenu: true },
+const NAV_ITEMS = [
+ { key: "nav.shop", route: ShopRoute, icon: shopIcon },
+ { key: "nav.apiary", route: ApiaryRoute, icon: apiaryIcon },
+ { key: "nav.game", route: GameRoute, icon: gameIcon },
+ { key: "nav.cashdesk", route: CashdeskRoute, icon: cashdeskIcon },
+ { key: "nav.menu", isMenu: true, icon: menuIcon },
];
+const HORIZONTAL_ENTRANCE_DELAYS = [0.2, 0.1, 0, 0.1, 0.2];
-function IconPlaceholder() {
- return
;
-}
+const MENU_ITEMS = [
+ { key: "nav.roulette", route: RouletteRoute, icon: rouletteIcon, delay: 0.1 },
+ { key: "nav.tasks", route: TasksRoute, icon: tasksIcon, delay: 0.05 },
+ { key: "nav.earnings", route: EarningsRoute, icon: earningsIcon, delay: 0 },
+];
type BarProps = {
labelKey: string;
+ icon: string;
active: boolean;
entranceDelay: number;
+ onClick: () => void;
};
-function NavBar({ labelKey, active, entranceDelay }: BarProps) {
+function NavBar({ labelKey, icon, active, entranceDelay, onClick }: BarProps) {
+ const { t } = useTranslation();
+ const isInitial = useRef(true);
+
+ useEffect(() => {
+ isInitial.current = false;
+ }, []);
+
+ return (
+
+
+

+
+ {t(labelKey)}
+
+
+
+ );
+}
+
+type MenuBarProps = {
+ labelKey: string;
+ icon: string;
+ delay: number;
+ onClick: () => void;
+};
+
+const MENU_SPRING = { type: "spring" as const, stiffness: 400, damping: 20 };
+
+function MenuBar({ labelKey, icon, delay, onClick }: MenuBarProps) {
const { t } = useTranslation();
return (
-
-
-
-
-
- {t(labelKey)}
-
-
-
-
-
+
+

+
{t(labelKey)}
+
+
);
}
export default function Navigation() {
const matchRoute = useMatchRoute();
+ const navigate = useNavigate();
+ const [menuOpen, setMenuOpen] = useState(false);
+ const navRef = useRef(null);
+
+ const handleOutsideClick = useCallback((e: MouseEvent) => {
+ if (navRef.current && !navRef.current.contains(e.target as Node)) {
+ setMenuOpen(false);
+ }
+ }, []);
+
+ useEffect(() => {
+ if (menuOpen) {
+ document.addEventListener("click", handleOutsideClick);
+ return () => document.removeEventListener("click", handleOutsideClick);
+ }
+ }, [menuOpen, handleOutsideClick]);
return (
-