diff --git a/.eslintrc.json b/.eslintrc.json index e37e1e072..c08a50b84 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,8 +2,13 @@ "extends": ["eslint:recommended", "prettier", "react-app", "react-app/jest"], "plugins": ["prettier"], "rules": { - "camelcase": ["error", { "properties": "never" }], - "prettier/prettier": "error", + "camelcase": ["error"], + "prettier/prettier": [ + "error", + { + "endOfLine": "auto" + } + ], "eqeqeq": ["error", "always"], "no-unused-vars": ["error"] } diff --git a/.prettierrc.js b/.prettierrc.js index 65e18f5ff..1cccb3ea5 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,4 +1,5 @@ module.exports = { + endOfLine: "auto", printWidth: 120, tabWidth: 2, useTabs: false, diff --git a/src/api.js b/src/api.js new file mode 100644 index 000000000..8f1a309f5 --- /dev/null +++ b/src/api.js @@ -0,0 +1,29 @@ +const hostUrl = "https://wedev-api.sky.pro/api/v2/leaderboard"; +export async function getLeaders() { + const response = await fetch(hostUrl, { + method: "GET", + }); + if (!response.status === 200) { + throw new Error("Не удалось получить список лидеров"); + } + const data = await response.json(); + return data; +} + +export async function addLeaders({ name, time, achievements }) { + const response = await fetch(hostUrl, { + method: "POST", + body: JSON.stringify({ + name, + time, + achievements, + }), + }); + if (!response.status === 201) { + throw new Error("Не удалось добавить в список лидеров"); + } else if (response.status === 400) { + throw new Error("Введите Ваше имя"); + } + const data = await response.json(); + return data; +} diff --git a/src/components/Button/Button.module.css b/src/components/Button/Button.module.css index 5d3f1f80e..34e78e477 100644 --- a/src/components/Button/Button.module.css +++ b/src/components/Button/Button.module.css @@ -10,7 +10,7 @@ font-variant-numeric: lining-nums proportional-nums; /* Pres → Caption S */ - font-family: StratosSkyeng; + font-family: Roboto; font-size: 24px; font-style: normal; font-weight: 400; diff --git a/src/components/Card/Card.module.css b/src/components/Card/Card.module.css index 86c3fbb5b..97c538c60 100644 --- a/src/components/Card/Card.module.css +++ b/src/components/Card/Card.module.css @@ -40,7 +40,7 @@ color: #000; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 24px; font-style: normal; font-weight: 400; diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7526a56c8..cabe8aa00 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -5,6 +5,8 @@ import styles from "./Cards.module.css"; import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; +import CardsIcon from "../../icon/cards.svg"; +import ToolsComponent from "../Tools/ToolsComponent"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -40,7 +42,7 @@ function getTimerValue(startDate, endDate) { * pairsCount - сколько пар будет в игре * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ -export function Cards({ pairsCount = 3, previewSeconds = 5 }) { +export function Cards({ pairsCount = 3, previewSeconds = 5, isGameMode }) { // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); // Текущий статус игры @@ -57,6 +59,15 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { minutes: 0, }); + // Стейт для счетчика попыток + const [numberOfAttempts, setNumberOfAttempts] = useState(2); + + const [achievements, setAchievements] = useState([]); + + const minusOneAttempt = () => { + setNumberOfAttempts(numberOfAttempts - 1); + }; + function finishGame(status = STATUS_LOST) { setGameEndDate(new Date()); setStatus(status); @@ -68,11 +79,14 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTimer(getTimerValue(startDate, null)); setStatus(STATUS_IN_PROGRESS); } + //добавлено кол-во попыток setNumberOfAttempts и ачивки setAchievements function resetGame() { setGameStartDate(null); setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); + setNumberOfAttempts(2); + setAchievements(prevState => prevState.filter(achieve => achieve !== 2)); } /** @@ -125,17 +139,47 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const playerLost = openCardsWithoutPair.length >= 2; - // "Игрок проиграл", т.к на поле есть две открытые карты без пары - if (playerLost) { - finishGame(STATUS_LOST); - return; - } - // ... игра продолжается + if (isGameMode === "true") { + if (playerLost) { + minusOneAttempt(); + if (numberOfAttempts < 1) { + finishGame(STATUS_LOST); + return; + } else { + setTimeout(() => { + setCards(cards.map(card => (openCardsWithoutPair.includes(card) ? { ...card, open: false } : card))); + }, 1000); + } + } + } else { + if (playerLost) { + finishGame(STATUS_LOST); + return; + } + } }; const isGameEnded = status === STATUS_LOST || status === STATUS_WON; + //Ачивка на открытие пары карт + const alohomora = () => { + if (achievements.includes(2)) { + alert("Подсказкой можно воспользоваться только 1 раз"); + return; + } + + let closedCards = cards.filter(card => !card.open); + closedCards = shuffle(closedCards); + closedCards[0].open = true; + closedCards.forEach(card => { + if (card.suit === closedCards[0].suit && card.rank === closedCards[0].rank) { + card.open = true; + } + }); + setAchievements(prevState => [...prevState, 2]); + }; + // Игровой цикл useEffect(() => { // В статусах кроме превью доп логики не требуется @@ -171,6 +215,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { clearInterval(intervalId); }; }, [gameStartDate, gameEndDate]); + //Отслеживание состояния использования ачивки + useEffect(() => { + if (!isGameMode) { + setAchievements(prevState => [...prevState, 1]); + } + }, [isGameMode]); return (
@@ -195,7 +245,17 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )}
- {status === STATUS_IN_PROGRESS ? : null} + {status === STATUS_IN_PROGRESS ? ( + <> + {isGameMode === "true" ? ( +
осталось попыток: {numberOfAttempts + 1}
+ ) : null} + + Открыть пару карточек + + + + ) : null}
@@ -217,6 +277,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { gameDurationSeconds={timer.seconds} gameDurationMinutes={timer.minutes} onClick={resetGame} + isGameMode={isGameMode} />
) : null} diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 000c5006c..851d70320 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -37,7 +37,7 @@ align-items: end; color: #fff; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 64px; font-style: normal; font-weight: 400; @@ -62,7 +62,7 @@ .timerDescription { color: #fff; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 16px; font-style: normal; font-weight: 400; @@ -70,3 +70,14 @@ margin-bottom: -12px; } + +.attemptСounter { + text-align: center; + color: #fff; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 32px; +} diff --git a/src/components/Context/Context.js b/src/components/Context/Context.js new file mode 100644 index 000000000..9c093b0dd --- /dev/null +++ b/src/components/Context/Context.js @@ -0,0 +1,9 @@ +import { createContext, useState } from "react"; + +export const EasyModeContext = createContext(); + +export const GameProvider = ({ children }) => { + const [selectedLevel, setSelectedLevel] = useState(null); + + return {children}; +}; diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 722394833..2f7852685 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -1,13 +1,68 @@ import styles from "./EndGameModal.module.css"; - import { Button } from "../Button/Button"; - import deadImageUrl from "./images/dead.png"; import celebrationImageUrl from "./images/celebration.png"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import { useState } from "react"; +import { addLeaders } from "../../api"; export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, onClick }) { - const title = isWon ? "Вы победили!" : "Вы проиграли!"; + //const title = isWon ? "Вы победили!" : "Вы проиграли!"; -изменно + const maxLevel = 9; // Переменная для числа + const { pairsCount } = useParams(); + const isMaxLevel = pairsCount === maxLevel; // Переменная для условия + + const navigate = useNavigate(); + const gameSeconds = gameDurationMinutes * 60 + gameDurationSeconds; + const [userData, setuserData] = useState({ + name: "", + time: gameSeconds, + achievements: [], + }); + //если isGameMode true то в userData.achievements нужно добавить 1 + + const handleInputChange = e => { + const { name, value } = e.target; // Извлекаем имя поля и его значение + setuserData({ + ...userData, // Копируем текущие данные из состояния + [name]: value, // Обновляем нужное поле + }); + }; + + async function handleAddUser(e) { + e.preventDefault(); + try { + await addLeaders(userData).then(data => { + navigate(`/leaderboard`); + }); + } catch (error) { + alert(error.message); + } + } + async function handleAddUserButton(e) { + e.preventDefault(); + try { + await addLeaders(userData).then(data => { + onClick(); + }); + } catch (error) { + alert(error.message); + } + } + + let title = ""; + if (isMaxLevel) { + // Используем переменную вместо условия + title = isWon ? "Вы попали на лидерборд!" : "Вы проиграли!"; + } else { + title = isWon ? "Вы победили!" : "Вы проиграли!"; + } + + const startTheGame = e => { + e.preventDefault(); + navigate(`/`); + }; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; @@ -16,12 +71,35 @@ export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes,
{imgAlt}

{title}

+ {isMaxLevel && isWon ? ( +
+ +
+ ) : null}

Затраченное время:

{gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")}
- - + {isMaxLevel && isWon ? ( + <> + + + Перейти к лидерборду + + + ) : ( + <> + + + )}
); } diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 9368cb8b5..ecf8a61a1 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,6 +1,7 @@ .modal { width: 480px; - height: 459px; + padding-top: 30px; + padding-bottom: 30px; border-radius: 12px; background: #c2f5ff; display: flex; @@ -18,19 +19,19 @@ .title { color: #004980; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 40px; font-style: normal; font-weight: 400; line-height: 48px; - + text-align: center; margin-bottom: 28px; } .description { color: #000; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 24px; font-style: normal; font-weight: 400; @@ -41,7 +42,7 @@ .time { color: #000; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 64px; font-style: normal; font-weight: 400; @@ -49,3 +50,62 @@ margin-bottom: 40px; } +.leaderBoardLink { + padding-top: 10px; + padding-bottom: 10px; + color: #004980; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 32px; + text-decoration: underline; +} + +.leaderBoardLink:hover { + text-decoration: none; +} + +.form { + padding-top: 10px; + padding-bottom: 20px; +} +.nameInput { + width: 276px; + height: 45px; + border-radius: 10px; + border: none; + color: #999999; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; +} + +.leaderBoardLinkBox { + color: red; + font-family: Roboto; + text-decoration: none; + text-transform: uppercase; + padding: 10px; +} + +.leaderBoardLinkBox:hover { + text-decoration: underline; +} + +.leaderBoardLinkBox:active { + color: black; +} + +.leaderBoardLinkBox:visited { + color: purple; +} +.LeaderBoardGlob { + margin: 15px; +} diff --git a/src/components/Tools/ToolsComponent.jsx b/src/components/Tools/ToolsComponent.jsx new file mode 100644 index 000000000..422deb640 --- /dev/null +++ b/src/components/Tools/ToolsComponent.jsx @@ -0,0 +1,25 @@ +import React, { useRef, useState } from "react"; +import styles from "./ToolsComponent.module.css"; + +const ToolsComponent = ({ children, text, customClass }) => { + const refSetTimeout = useRef(); + const [showToolTip, setShowToolTip] = useState(false); + const toolTipClasses = customClass ? `${styles.tooltip} ${customClass}` : `${styles.tooltip}`; + const onMouseEnterHandler = () => { + refSetTimeout.current = setTimeout(() => { + setShowToolTip(true); + }, 750); + }; + const onMouseLeaveHandler = () => { + clearTimeout(refSetTimeout.current); + setShowToolTip(false); + }; + return ( +
+ {children} + {showToolTip &&
{text}
} +
+ ); +}; + +export default ToolsComponent; diff --git a/src/components/Tools/ToolsComponent.module.css b/src/components/Tools/ToolsComponent.module.css new file mode 100644 index 000000000..6a5b4ed1a --- /dev/null +++ b/src/components/Tools/ToolsComponent.module.css @@ -0,0 +1,23 @@ +.container { + position: relative; + display: flex; +} + +.tooltip { + position: absolute; + + padding: 20px 20px; + margin-top: 80px; + justify-content: center; + color: #004980; + background-color: #c2f5ff; + font-family: Roboto; + border-radius: 12px; + text-align: center; + white-space: pre-line; + font-size: 18px; + line-height: 25px; + font-weight: 400; + pointer-events: none; + z-index: 99999999; +} diff --git a/src/icon/cards.svg b/src/icon/cards.svg new file mode 100644 index 000000000..08a4dc8a2 --- /dev/null +++ b/src/icon/cards.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/index.css b/src/index.css index 78f0d3a2b..a0f94c4cf 100644 --- a/src/index.css +++ b/src/index.css @@ -28,7 +28,7 @@ ol { } @font-face { - font-family: "StratosSkyeng"; + font-family: Roboto; src: url("../public/assets/fonts/StratosSkyeng.woff2") format("woff2"), local("Arial"); diff --git a/src/index.js b/src/index.js index f689c5f0b..741064575 100644 --- a/src/index.js +++ b/src/index.js @@ -3,10 +3,13 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import { RouterProvider } from "react-router-dom"; import { router } from "./router"; +import { GameProvider } from "./components/Context/Context"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/GamePage/GamePage.jsx b/src/pages/GamePage/GamePage.jsx index a4be871db..84cc083e5 100644 --- a/src/pages/GamePage/GamePage.jsx +++ b/src/pages/GamePage/GamePage.jsx @@ -4,10 +4,10 @@ import { Cards } from "../../components/Cards/Cards"; export function GamePage() { const { pairsCount } = useParams(); - + const { isGameMode } = useParams(); return ( <> - + ); } diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx new file mode 100644 index 000000000..62d34e132 --- /dev/null +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -0,0 +1,54 @@ +import styles from "./LeaderBoardPage.module.css"; +import { useEffect, useState } from "react"; +import { Button } from "../../components/Button/Button"; +import { useNavigate } from "react-router-dom"; +import { getLeaders } from "../../api"; + +export function LeaderBoardPage() { + const [leaders, setLeaders] = useState([]); + useEffect(() => { + getLeaders().then(leadersList => { + setLeaders(leadersList.leaders); + }); + }, []); + const navigate = useNavigate(); + const startTheGame = e => { + e.preventDefault(); + navigate(`/`); + }; + + let i = 1; + + return ( +
+
+

Лидерборд

+ +
+ +
+ ); +} diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css new file mode 100644 index 000000000..e236f2505 --- /dev/null +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css @@ -0,0 +1,124 @@ +* { + margin: 0; + padding: 0; +} + +.container { + padding: 40px; + padding-left: 25%; + padding-right: 25%; + padding-top: 15%; +} + +.header { + display: flex; + justify-content: space-between; +} + +.leaderBoard { + padding-top: 40px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.headerTitle { + color: #ffffff; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; + text-align: center; +} + +.leadersItemTitle { + color: #999999; + list-style-type: none; + background-color: #ffffff; + padding: 10px 20px; + border-radius: 12px; + display: flex; + justify-content: space-between; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; +} + +.leadersItem { + list-style-type: none; + background-color: #ffffff; + padding: 10px 20px; + border-radius: 12px; + display: flex; + justify-content: space-between; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; +} +.iconBtn:hover { + cursor: pointer; +} +.nameBox { + /* min-width: 150px; */ + /* max-width: 150px; */ + overflow-wrap: break-word; + white-space: normal; + min-width: 300px; + display: flex; + align-items:start; + +} +.numberBox { + display: block; + min-width: 70px; + max-width: 70px; + overflow-wrap: break-word; + white-space: normal; + margin-right: 25%; + +} + +.timeBox{ + min-width: 100px; + display: flex; + justify-content: flex-end; + padding-right: 2%; + +} + +.achivBox { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + min-width: 80px; + max-width: 80px; + overflow-wrap: break-word; + white-space: normal; +} +.toolsCustom { + position: absolute; + min-width: 180px; + padding: 20px 20px; + margin-top: 80px; + justify-content: center; + color: #004980; + background-color: #c2f5ff; + font-family: Roboto; + border-radius: 12px; + text-align: center; + white-space: pre-line; + font-size: 18px; + line-height: 25px; + font-weight: 400; + pointer-events: none; + z-index: 99999999; +} diff --git a/src/pages/LeaderBoardPage/imgLeader/iconCardMinus.svg b/src/pages/LeaderBoardPage/imgLeader/iconCardMinus.svg new file mode 100644 index 000000000..1a29eb93c --- /dev/null +++ b/src/pages/LeaderBoardPage/imgLeader/iconCardMinus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/pages/LeaderBoardPage/imgLeader/iconCardPlus.svg b/src/pages/LeaderBoardPage/imgLeader/iconCardPlus.svg new file mode 100644 index 000000000..5cfaa9bd4 --- /dev/null +++ b/src/pages/LeaderBoardPage/imgLeader/iconCardPlus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 758942e51..2c3ac5b94 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,28 +1,81 @@ -import { Link } from "react-router-dom"; -import styles from "./SelectLevelPage.module.css"; +import { Link, useNavigate } from "react-router-dom"; +import { useContext, useState } from "react"; +import styles from "../SelectLevelPage/SelectLevelPage.module.css"; +import { EasyModeContext } from "../../components/Context/Context"; export function SelectLevelPage() { + //Выбор легкого-сложного режима через контекст + const { selectedLevel, setSelectedLevel } = useContext(EasyModeContext); // Состояние для хранения выбранного уровня + const [isEasyMode, setIsEasyMode] = useState(false); // Локальное состояние для отслеживания режима игры + //const [checked, setChecked] = useState(false); //отслеживания режима игры + const navigate = useNavigate(); // Хук для навигации + const handleStartGame = () => { + if (selectedLevel !== null) { + navigate(`/game/${selectedLevel}/${isEasyMode ? true : false}`); + //navigate(`/game/${selectedLevel}/${checked ? true : false}`); // Переход на страницу игры с выбранным уровнем + } else { + alert("Пожалуйста, выберите уровень!"); // Предупреждение, если уровень не выбран + } + }; + return (

Выбери сложность

+
+ setIsEasyMode(!isEasyMode)} // переключалка режима игры при каждом изменении + id="easyMode" + /> + +
+ +
+ + Перейти к лидерборду + +
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 390ac0def..56664b005 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -21,7 +21,7 @@ color: #004980; text-align: center; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 40px; font-style: normal; font-weight: 400; @@ -35,6 +35,10 @@ margin-top: 48px; margin-bottom: 28px; } +.selected { + background: #56df8f; /* Цвет для выделенной кнопки */ + color: #fff; /* Цвет текста для выделенной кнопки */ +} .level { display: flex; @@ -51,14 +55,88 @@ .levelLink { color: #0080c1; text-align: center; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 64px; font-style: normal; font-weight: 400; - line-height: 72px; + line-height: 97px; text-decoration: none; + border-radius: 12px; } .levelLink:visited { color: #0080c1; } +.easyMode { + font-family: Roboto; + font-size: 20px; +} +.easyMode::after { + content: ""; + display: block; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0); + width: 12px; + height: 12px; + background-color: blue; + border-radius: 3px; + transition: 0.3s; +} +.checkbox-input { + display: none; + &:checked + label .checkbox { + &:after { + transform: translate(-50%, -50%) scale(1); + } + } +} +.button { + width: 246px; + height: 48px; + margin: 15px; + border-radius: 12px; + background: #56df8f; + + color: #fff; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + + font-family: Roboto; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; + + border: none; + + cursor: pointer; +} + +.button:hover { + background: #00d41ccc; +} + +.leaderBoardLinkBox { + color: red; + font-family: Roboto; + text-decoration: none; + text-transform: uppercase; + padding: 10px; +} + +.leaderBoardLinkBox:hover { + text-decoration: underline; +} + +.leaderBoardLinkBox:active { + color: black; +} + +.leaderBoardLinkBox:visited { + color: purple; +} +.LeaderBoardGlob { + margin: 15px; +} diff --git a/src/router.js b/src/router.js index da6e94b51..7740fee09 100644 --- a/src/router.js +++ b/src/router.js @@ -1,6 +1,7 @@ import { createBrowserRouter } from "react-router-dom"; import { GamePage } from "./pages/GamePage/GamePage"; import { SelectLevelPage } from "./pages/SelectLevelPage/SelectLevelPage"; +import { LeaderBoardPage } from "./pages/LeaderBoardPage/LeaderBoardPage"; export const router = createBrowserRouter( [ @@ -9,13 +10,17 @@ export const router = createBrowserRouter( element: , }, { - path: "/game/:pairsCount", + path: "/game/:pairsCount/:isGameMode", element: , }, + { + path: "/LeaderBoard", + element: , + }, ], - /** - * basename нужен для корректной работы в gh pages - * он же указан в homepage package.json и в index.html - */ - { basename: "/react-memo" }, + { + // basename нужен для корректной работы в gh pages + // он же указан в homepage package.json и в index.html + basename: "/react-memo", + }, );