From 26554e05a7484ea24c8e09ded3f3e489c79d1603 Mon Sep 17 00:00:00 2001 From: KharybinRuslan Date: Mon, 16 Sep 2024 22:20:07 +0300 Subject: [PATCH 1/4] New work --- .prettierrc.js | 1 + docs/mvp-spec.md | 7 +- package-lock.json | 12 +- public/Vector.svg | 3 + public/robots.txt | 2 +- src/api/getLeaderboard.js | 14 +++ src/api/postLeaderboard.js | 23 ++++ src/components/Cards/Cards.jsx | 104 ++++++++++++++-- src/components/Cards/Cards.module.css | 17 +++ src/components/EndGameModal/EndGameModal.jsx | 115 ++++++++++++++++-- .../EndGameModal/EndGameModal.module.css | 50 +++++++- src/contexte/contexte.jsx | 32 +++++ src/index.js | 5 +- src/pages/Leaderboard/Leaderboard.jsx | 35 ++++++ src/pages/Leaderboard/Leaderboard.module.css | 51 ++++++++ src/pages/Leaderboard/LeaderboardPage.jsx | 19 +++ .../SelectLevelPage/SelectLevelButton.jsx | 17 +++ src/pages/SelectLevelPage/SelectLevelPage.jsx | 69 ++++++++--- .../SelectLevelPage.module.css | 93 +++++++++++++- src/router.js | 5 + 20 files changed, 622 insertions(+), 52 deletions(-) create mode 100644 public/Vector.svg create mode 100644 src/api/getLeaderboard.js create mode 100644 src/api/postLeaderboard.js create mode 100644 src/contexte/contexte.jsx create mode 100644 src/pages/Leaderboard/Leaderboard.jsx create mode 100644 src/pages/Leaderboard/Leaderboard.module.css create mode 100644 src/pages/Leaderboard/LeaderboardPage.jsx create mode 100644 src/pages/SelectLevelPage/SelectLevelButton.jsx diff --git a/.prettierrc.js b/.prettierrc.js index 65e18f5ff..28033a2b6 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -7,4 +7,5 @@ module.exports = { bracketSpacing: true, arrowParens: "avoid", htmlWhitespaceSensitivity: "ignore", + endOfLine: "auto", }; diff --git a/docs/mvp-spec.md b/docs/mvp-spec.md index fab47685e..e083d7788 100644 --- a/docs/mvp-spec.md +++ b/docs/mvp-spec.md @@ -14,9 +14,10 @@ Количество карточек для каждого уровня сложности можете назначать и свои или выбрать готовый пресет. Предлагаем следующее пресеты: - - Легкий уровень - 6 карточек (3 пары) - - Средний уровень - 12 карточек (6 пар) - - Сложный уровень - 18 карточек (9 пар) + +- Легкий уровень - 6 карточек (3 пары) +- Средний уровень - 12 карточек (6 пар) +- Сложный уровень - 18 карточек (9 пар) Как только уровень сложности выбран, игроку показывается на игровой поле. diff --git a/package-lock.json b/package-lock.json index edaf5083f..dd3e2dd10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5961,9 +5961,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "funding": [ { "type": "opencollective", @@ -22559,9 +22559,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001522", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", - "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==" + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==" }, "case-sensitive-paths-webpack-plugin": { "version": "2.4.0", diff --git a/public/Vector.svg b/public/Vector.svg new file mode 100644 index 000000000..dc94e72f4 --- /dev/null +++ b/public/Vector.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/robots.txt b/public/robots.txt index e9e57dc4d..5537f0739 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,3 +1,3 @@ # https://www.robotstxt.org/robotstxt.html User-agent: * -Disallow: +Disallow: \ No newline at end of file diff --git a/src/api/getLeaderboard.js b/src/api/getLeaderboard.js new file mode 100644 index 000000000..fd64ba158 --- /dev/null +++ b/src/api/getLeaderboard.js @@ -0,0 +1,14 @@ +const apiUrl = "https://wedev-api.sky.pro/api/leaderboard"; + +export const getLeaderboard = async () => { + // Запрос к API получения списка победителей + const response = await fetch(apiUrl, { + method: "GET", + }); + + if (!response.ok) { + throw new Error(`Не удалось получить данные с сервера! status: ${response.status}`); + } + + return await response.json(); +}; diff --git a/src/api/postLeaderboard.js b/src/api/postLeaderboard.js new file mode 100644 index 000000000..a3eb0b40e --- /dev/null +++ b/src/api/postLeaderboard.js @@ -0,0 +1,23 @@ +const apiUrl = "https://wedev-api.sky.pro/api/leaderboard"; + +export const postLeaderboard = async ({ userName, userTime }) => { + // Запрос к API отправки победителя + const response = await fetch(apiUrl, { + method: "POST", + body: JSON.stringify({ name: goodByeHacker(userName), time: userTime }), + }); + + if (!response.ok) { + throw new Error(`Не удалось отправить данные на сервер! status: ${response.status}`); + } + + return await response.json(); +}; + +function goodByeHacker(text) { + return text + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll("QUOTE_BEGIN", "
") + .replaceAll("QUOTE_END", "
"); +} diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7526a56c8..bdb9ef3e2 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -1,14 +1,18 @@ import { shuffle } from "lodash"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { generateDeck } from "../../utils/cards"; 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 { EasyContext } from "../../contexte/contexte"; +import { useNavigate } from "react-router-dom"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; const STATUS_WON = "STATUS_WON"; +// Пауза игры при допускании ошибки выбора карточки +const STATUS_PAUSED = "STATUS_PAUSED"; // Идет игра: карты закрыты, игрок может их открыть const STATUS_IN_PROGRESS = "STATUS_IN_PROGRESS"; // Начало игры: игрок видит все карты в течении нескольких секунд @@ -19,6 +23,7 @@ function getTimerValue(startDate, endDate) { return { minutes: 0, seconds: 0, + diffInSecconds: 0, }; } @@ -27,11 +32,13 @@ function getTimerValue(startDate, endDate) { } const diffInSecconds = Math.floor((endDate.getTime() - startDate.getTime()) / 1000); + const minutes = Math.floor(diffInSecconds / 60); const seconds = diffInSecconds % 60; return { minutes, seconds, + diffInSecconds, }; } @@ -41,8 +48,18 @@ function getTimerValue(startDate, endDate) { * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { + // Когда игра окончена, переход на главную страницу + const navigate = useNavigate(); + function goTo() { + navigate("/"); + } + + // Обработка количества попыток + const { tries, setTries, isEasyMode, checkedLevel, leadrs, setLeaders } = useContext(EasyContext); + // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); + // Текущий статус игры const [status, setStatus] = useState(STATUS_PREVIEW); @@ -55,18 +72,35 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [timer, setTimer] = useState({ seconds: 0, minutes: 0, + diffInSecconds: 0, }); - function finishGame(status = STATUS_LOST) { + // Если количество попыток равно 0 устанавливается стату проиграл и игра заканчивается + useEffect(() => { + if (tries === 0) { + finishGame(STATUS_LOST); + } + }, [tries]); + + function finishGame(status) { setGameEndDate(new Date()); setStatus(status); } + + function pausedGame(status = STATUS_PAUSED) { + setStatus(status); + } + function startGame() { const startDate = new Date(); setGameEndDate(null); setGameStartDate(startDate); setTimer(getTimerValue(startDate, null)); setStatus(STATUS_IN_PROGRESS); + // Добавлена проверка на включенный режим 3-х попыток + if (!isEasyMode) { + setTries(1); + } } function resetGame() { setGameStartDate(null); @@ -75,6 +109,24 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setStatus(STATUS_PREVIEW); } + function сontinueGame(status = STATUS_IN_PROGRESS) { + setStatus(status); + } + + // Функция запускает разные сценарии для кнопки в модальном окне + function whatsNext() { + if (status === STATUS_PAUSED) { + сontinueGame(STATUS_IN_PROGRESS); + } + if (status === STATUS_LOST) { + goTo(); + setTries(3); + } + if (status === STATUS_WON) { + resetGame(); + } + } + /** * Обработка основного действия в игре - открытие карты. * После открытия карты игра может пепереходит в следующие состояния @@ -123,18 +175,34 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { return false; }); - const playerLost = openCardsWithoutPair.length >= 2; + const havMistake = openCardsWithoutPair.length >= 2; - // "Игрок проиграл", т.к на поле есть две открытые карты без пары - if (playerLost) { - finishGame(STATUS_LOST); - return; + // Если на поле есть две открытые карты без пары, то игра паузится и уменьшается количество попыток + function minusTries() { + setTries(prev => prev - 1); + } + + // "Игрок допустил ошибку", т.к на поле есть две открытые карты без пары + if (havMistake) { + minusTries(); + pausedGame(STATUS_PAUSED); } // ... игра продолжается }; - const isGameEnded = status === STATUS_LOST || status === STATUS_WON; + // Проверка на попадание в топ 10 игроков + function isTopTen() { + const isTenPlayers = leadrs.length === 10; + if (status === STATUS_WON && checkedLevel === 3) { + if (leadrs.at(-1).time > timer.diffInSecconds || (isTenPlayers && leadrs[9].time > timer.diffInSecconds)) { + return true; + } + } + return false; + } + + const isGameEnded = status === STATUS_LOST || status === STATUS_WON || status === STATUS_PAUSED; // Игровой цикл useEffect(() => { @@ -160,6 +228,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { return () => { clearTimeout(timerId); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [status, pairsCount, previewSeconds]); // Обновляем значение таймера в интервале @@ -167,6 +236,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const intervalId = setInterval(() => { setTimer(getTimerValue(gameStartDate, gameEndDate)); }, 300); + return () => { clearInterval(intervalId); }; @@ -195,7 +265,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )} - {status === STATUS_IN_PROGRESS ? : null} +
+ {isEasyMode && status === STATUS_IN_PROGRESS && ( + Осталось {tries} попытки! + )} + {status === STATUS_IN_PROGRESS ? : null} +
@@ -206,6 +281,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { open={status !== STATUS_IN_PROGRESS ? true : card.open} suit={card.suit} rank={card.rank} + status={STATUS_IN_PROGRESS} /> ))}
@@ -213,10 +289,16 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { {isGameEnded ? (
) : null} diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 000c5006c..07b4904ef 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -70,3 +70,20 @@ margin-bottom: -12px; } + +.attempt { + color: #fff; + font-variant-numeric: lining-nums proportional-nums; + font-family: StratosSkyeng; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 32px; +} + +.buttonContainer { + display: flex; + flex-direction: column; + align-items: flex-end; + row-gap: 12px; +} diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 722394833..edb824971 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -1,27 +1,120 @@ import styles from "./EndGameModal.module.css"; - import { Button } from "../Button/Button"; - import deadImageUrl from "./images/dead.png"; import celebrationImageUrl from "./images/celebration.png"; +import { postLeaderboard } from "../../api/postLeaderboard"; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; + +export function EndGameModal({ + isWon, + gameDurationSeconds, + gameDurationMinutes, + onClick, + tries, + checkedLevel, + isTopTen, + leaders, + setLeaders, + diffInSecconds, +}) { + const [inputValue, setInputValue] = useState(""); + + const navigate = useNavigate(); + function goTo() { + navigate("/Leaderboard"); + } + + function handleClick() { + postLeaderboard({ userName: inputValue, userTime: diffInSecconds }).then(leaderboard => { + setLeaders(leaderboard.leaders.sort((a, b) => a.time - b.time).slice(0, 10)); + }); + goTo(); + } -export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, onClick }) { - const title = isWon ? "Вы победили!" : "Вы проиграли!"; + let imgSrc; + let imgAlt; - const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; + if (isWon === "STATUS_PAUSED") { + isWon = "Вы допустили ошибку"; + imgSrc = deadImageUrl; + imgAlt = "dead emodji"; + } - const imgAlt = isWon ? "celebration emodji" : "dead emodji"; + // Модалка лидерборда + if (isTopTen) { + isWon = "Вы попали на лидерборд!"; + imgSrc = celebrationImageUrl; + imgAlt = "celebration emodji"; + } else if (isWon === "STATUS_WON") { + isWon = "Вы победили!"; + imgSrc = celebrationImageUrl; + imgAlt = "celebration emodji"; + } + + if (isWon === "STATUS_LOST") { + isWon = "Вы проиграли!"; + imgSrc = deadImageUrl; + imgAlt = "dead emodji"; + } + + if (tries === 0) { + isWon = "Вы проиграли!"; + imgSrc = deadImageUrl; + imgAlt = "dead emodji"; + } + + const title = isWon; return (
{imgAlt}

{title}

-

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

-
- {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} -
+ {isWon === "Вы допустили ошибку" &&

Оставшеся количество попыток:

} + {isWon === "Вы победили!" &&

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

} + {isWon === "Вы попали на лидерборд!" && ( + setInputValue(e.target.value)} + placeholder="Введите имя" + maxLength="20" + /> + )} + {isWon === "Вы попали на лидерборд!" &&

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

} + {isWon === "Вы проиграли!" &&

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

} + {isWon === "Вы допустили ошибку" && ( +
+

{tries}

+
+ )} + {isWon === "Вы победили!" && ( +
+ {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} +
+ )} + + {/* Модалка лидербборда */} + {isWon === "Вы попали на лидерборд!" && ( +
+ {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} +
+ )} + {isWon === "Вы проиграли!" && ( +
+ {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} +
+ )} + {isWon === "Вы допустили ошибку" && } + {isWon === "Вы победили!" && } + {isWon === "Вы попали на лидерборд!" && } + {isWon === "Вы проиграли!" && } - + {isWon === "Вы попали на лидерборд!" && ( + + )}
); } diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 9368cb8b5..b4b55c370 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,12 +1,13 @@ .modal { width: 480px; - height: 459px; + /* height: 459px; */ border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; + padding-block: 36px; } .image { @@ -25,6 +26,7 @@ line-height: 48px; margin-bottom: 28px; + text-align: center; } .description { @@ -49,3 +51,49 @@ margin-bottom: 40px; } + +.nameInput { + width: 276px; + height: 45px; + border-radius: 10px; + border: none; + box-sizing: border-box; + padding: 10px 14px 10px 14px; + margin-bottom: 28px; + color: #999999; + font-size: 24px; +} + +.nameInput:active, +:hover, +:focus { + outline: 0; + outline-offset: 0; +} + +.nameInput::placeholder { + color: #999999; + font-family: StratosSkyeng; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; + text-align: center; +} + +.gameButtonsLink { + background-color: transparent; + border: none; + font-family: Roboto; + font-size: 18px; + font-weight: 400; + line-height: 32px; + text-align: center; + color: #004980; + margin-top: 18px; +} + +.gameButtonsLink:hover { + text-decoration: underline; + cursor: pointer; +} diff --git a/src/contexte/contexte.jsx b/src/contexte/contexte.jsx new file mode 100644 index 000000000..9d90b1a9d --- /dev/null +++ b/src/contexte/contexte.jsx @@ -0,0 +1,32 @@ +import { createContext, useState, useEffect } from "react"; +import { getLeaderboard } from "../api/getLeaderboard"; + +export const EasyContext = createContext(false); + +export const EasyProvider = ({ children }) => { + const [tries, setTries] = useState(3); + const [isEasyMode, setIsEasyMode] = useState(false); + const [leadrs, setLeaders] = useState([]); + const [checkedLevel, setCheckedLevel] = useState(); + + useEffect(() => { + if (leadrs.length === 0) { + getData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + async function getData() { + const data = await getLeaderboard(); + const leaders = data.leaders.sort((a, b) => a.time - b.time).slice(0, 10); + setLeaders(leaders); + } + + return ( + + {children} + + ); +}; diff --git a/src/index.js b/src/index.js index f689c5f0b..d39f1dd9d 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 { EasyProvider } from "./contexte/contexte"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/Leaderboard/Leaderboard.jsx b/src/pages/Leaderboard/Leaderboard.jsx new file mode 100644 index 000000000..3dac563c2 --- /dev/null +++ b/src/pages/Leaderboard/Leaderboard.jsx @@ -0,0 +1,35 @@ +import { Button } from "../../components/Button/Button"; +import styles from "../Leaderboard/Leaderboard.module.css"; +import { useNavigate } from "react-router-dom"; +import { LeaderboardPage } from "./LeaderboardPage"; +import { useContext } from "react"; +import { EasyContext } from "../../contexte/contexte"; + +export function Leaderboard() { + const { leadrs } = useContext(EasyContext); + + const navigate = useNavigate(); + function goTo() { + navigate("/"); + } + + return ( +
+
+

Лидерборд

+
+ +
+
+
+

Позиция

+

Пользователь

+

Время

+
+ {leadrs?.length && + leadrs.map((leader, index) => ( + + ))} +
+ ); +} diff --git a/src/pages/Leaderboard/Leaderboard.module.css b/src/pages/Leaderboard/Leaderboard.module.css new file mode 100644 index 000000000..c43287db6 --- /dev/null +++ b/src/pages/Leaderboard/Leaderboard.module.css @@ -0,0 +1,51 @@ +.container { + max-width: 944px; + margin: 0 auto; + padding-top: 22px; + box-sizing: border-box; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 40px; +} + +.titleLeaderBoard { + font-family: Roboto; + color: #ffffff; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; +} + +.titleBoard { + width: 944px; + height: 64px; + background-color: #ffffff; + border-radius: 12px; + padding: 16px 20px 16px 20px; + box-sizing: border-box; + display: grid; + grid-template-columns: [start] 264px [line2] 564px [line3] 74px [end]; +} + +.title { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #999999; +} + +.titlePage { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #000000; +} diff --git a/src/pages/Leaderboard/LeaderboardPage.jsx b/src/pages/Leaderboard/LeaderboardPage.jsx new file mode 100644 index 000000000..ec2736f46 --- /dev/null +++ b/src/pages/Leaderboard/LeaderboardPage.jsx @@ -0,0 +1,19 @@ +import styles from "../Leaderboard/Leaderboard.module.css"; + +export function LeaderboardPage({ id, name, time }) { + const minutes = Math.floor(time / 60); + const seconds = time % 60; + + return ( +
+
+

#{id}

+

{name.slice(0, 20)}

+ +

+ {minutes.toString().padStart("2", "0")}.{seconds.toString().padStart("2", "0")} +

+
+
+ ); +} diff --git a/src/pages/SelectLevelPage/SelectLevelButton.jsx b/src/pages/SelectLevelPage/SelectLevelButton.jsx new file mode 100644 index 000000000..b2c0f1c10 --- /dev/null +++ b/src/pages/SelectLevelPage/SelectLevelButton.jsx @@ -0,0 +1,17 @@ +import styles from "./SelectLevelPage.module.css"; +export function SelectLevelButton({ checkedLevel, levelNumber }) { + return ( + <> + {checkedLevel !== levelNumber && ( +
+ {levelNumber} +
+ )} + {checkedLevel === levelNumber && ( +
+ {levelNumber} +
+ )} + + ); +} diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 758942e51..cf1fbd143 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,28 +1,63 @@ -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; +import { useContext } from "react"; +import { EasyContext } from "../../contexte/contexte"; +import { Button } from "../../components/Button/Button"; +import { SelectLevelButton } from "./SelectLevelButton"; export function SelectLevelPage() { + const { isEasyMode, setIsEasyMode, checkedLevel, setCheckedLevel } = useContext(EasyContext); + + const handleClick = id => { + setCheckedLevel(Number(id)); + }; + + const navigate = useNavigate(); + function goTo() { + if (!checkedLevel) { + alert("Необходимо выбрать уровень игры"); + } else { + navigate(`/game/${checkedLevel * 3}`); + } + } + return (
-

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

+

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

    -
  • - - 1 - -
  • -
  • - - 2 - -
  • -
  • - - 3 - -
  • + + +
+ + +
+ + +

Перейти к лидерборду

+ +
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 390ac0def..81aa13230 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -8,13 +8,14 @@ .modal { width: 480px; - height: 459px; + /* height: 459px; */ border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; + padding-block: 36px; } .title { @@ -46,6 +47,22 @@ border-radius: 12px; background: #fff; + cursor: pointer; +} + +.levelChek { + display: flex; + width: 97px; + height: 98px; + flex-direction: column; + justify-content: center; + flex-shrink: 0; + + border-radius: 12px; + background: #fff; + border: 4px solid #0080c1; + box-sizing: border-box; + cursor: pointer; } .levelLink { @@ -62,3 +79,77 @@ .levelLink:visited { color: #0080c1; } + +.isEasyMode { + display: flex; + align-items: center; + box-sizing: border-box; + padding: 8px 16px 8px 16px; + border-radius: 12px; + background-color: #fff; +} + +.isEasyModeTitle { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; + cursor: pointer; +} + +.checkbox { + display: none; +} + +.customCheckbox { + position: relative; + display: inline-block; + width: 30px; + height: 30px; + background-color: #fff; + margin-right: 8px; + vertical-align: sub; + border-radius: 5px; + cursor: pointer; +} + +.customCheckbox::before { + content: ""; + display: inline-block; + width: 20px; + height: 20px; + background-image: url(/public/Vector.svg); + background-size: contain; + background-repeat: no-repeat; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + margin-top: 2px; +} + +.checkbox:checked + .customCheckbox::before { + transform: translate(-50%, -50%) scale(1); +} + +.gameButtonsContainer { + display: flex; + flex-direction: column; + align-items: center; + gap: 18px; + margin-top: 38px; +} + +.gameButtonsLink { + font-family: Roboto; + font-size: 18px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #004980; +} + +.radio { + display: none; +} diff --git a/src/router.js b/src/router.js index da6e94b51..4826abf63 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 { Leaderboard } from "./pages/Leaderboard/Leaderboard"; export const router = createBrowserRouter( [ @@ -12,6 +13,10 @@ export const router = createBrowserRouter( path: "/game/:pairsCount", element: , }, + { + path: "/leaderboard", + element: , + }, ], /** * basename нужен для корректной работы в gh pages From 606c883c0af54b23290390501315ea17c28d62cb Mon Sep 17 00:00:00 2001 From: KharybinRuslan Date: Mon, 16 Sep 2024 22:24:42 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/contexte/contexte.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/contexte/contexte.jsx b/src/contexte/contexte.jsx index 9d90b1a9d..14360784d 100644 --- a/src/contexte/contexte.jsx +++ b/src/contexte/contexte.jsx @@ -22,6 +22,8 @@ export const EasyProvider = ({ children }) => { setLeaders(leaders); } + + return ( Date: Tue, 17 Sep 2024 20:21:32 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 2 +- .prettierrc.js | 3 +- lefthook.yml | 2 +- public/manifest.json | 2 +- src/api/getLeaderboard.js | 14 --- src/api/postLeaderboard.js | 23 ----- src/components/Button/Button.module.css | 2 +- src/components/Card/Card.module.css | 2 +- src/components/Cards/Cards.jsx | 24 +---- src/components/Cards/Cards.module.css | 2 +- src/components/EndGameModal/EndGameModal.jsx | 70 ++------------- .../EndGameModal/EndGameModal.module.css | 53 +---------- src/contexte/contexte.jsx | 29 +----- src/index.css | 2 +- src/pages/GamePage/GamePage.jsx | 2 +- src/pages/Leaderboard/Leaderboard.jsx | 35 -------- src/pages/Leaderboard/Leaderboard.module.css | 51 ----------- src/pages/Leaderboard/LeaderboardPage.jsx | 19 ---- .../SelectLevelPage/SelectLevelButton.jsx | 17 ---- src/pages/SelectLevelPage/SelectLevelPage.jsx | 64 +++++--------- .../SelectLevelPage.module.css | 88 +++---------------- src/router.js | 7 +- src/utils/cards.js | 2 +- 23 files changed, 59 insertions(+), 456 deletions(-) delete mode 100644 src/api/getLeaderboard.js delete mode 100644 src/api/postLeaderboard.js delete mode 100644 src/pages/Leaderboard/Leaderboard.jsx delete mode 100644 src/pages/Leaderboard/Leaderboard.module.css delete mode 100644 src/pages/Leaderboard/LeaderboardPage.jsx delete mode 100644 src/pages/SelectLevelPage/SelectLevelButton.jsx diff --git a/.eslintrc.json b/.eslintrc.json index e37e1e072..df89451f1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,4 +7,4 @@ "eqeqeq": ["error", "always"], "no-unused-vars": ["error"] } -} +} \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js index 28033a2b6..99cd00a0a 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -7,5 +7,4 @@ module.exports = { bracketSpacing: true, arrowParens: "avoid", htmlWhitespaceSensitivity: "ignore", - endOfLine: "auto", -}; +}; \ No newline at end of file diff --git a/lefthook.yml b/lefthook.yml index 87752f862..bf049dd55 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -17,4 +17,4 @@ pre-commit: commands: eslint: glob: "*.{js,jsx}" - run: npm run lint + run: npm run lint \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json index 3bd1fa1b8..3a670d468 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -22,4 +22,4 @@ "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" -} +} \ No newline at end of file diff --git a/src/api/getLeaderboard.js b/src/api/getLeaderboard.js deleted file mode 100644 index fd64ba158..000000000 --- a/src/api/getLeaderboard.js +++ /dev/null @@ -1,14 +0,0 @@ -const apiUrl = "https://wedev-api.sky.pro/api/leaderboard"; - -export const getLeaderboard = async () => { - // Запрос к API получения списка победителей - const response = await fetch(apiUrl, { - method: "GET", - }); - - if (!response.ok) { - throw new Error(`Не удалось получить данные с сервера! status: ${response.status}`); - } - - return await response.json(); -}; diff --git a/src/api/postLeaderboard.js b/src/api/postLeaderboard.js deleted file mode 100644 index a3eb0b40e..000000000 --- a/src/api/postLeaderboard.js +++ /dev/null @@ -1,23 +0,0 @@ -const apiUrl = "https://wedev-api.sky.pro/api/leaderboard"; - -export const postLeaderboard = async ({ userName, userTime }) => { - // Запрос к API отправки победителя - const response = await fetch(apiUrl, { - method: "POST", - body: JSON.stringify({ name: goodByeHacker(userName), time: userTime }), - }); - - if (!response.ok) { - throw new Error(`Не удалось отправить данные на сервер! status: ${response.status}`); - } - - return await response.json(); -}; - -function goodByeHacker(text) { - return text - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll("QUOTE_BEGIN", "
") - .replaceAll("QUOTE_END", "
"); -} diff --git a/src/components/Button/Button.module.css b/src/components/Button/Button.module.css index 5d3f1f80e..132022b6f 100644 --- a/src/components/Button/Button.module.css +++ b/src/components/Button/Button.module.css @@ -23,4 +23,4 @@ .button:hover { background: #7ac100cc; -} +} \ No newline at end of file diff --git a/src/components/Card/Card.module.css b/src/components/Card/Card.module.css index 86c3fbb5b..9705b2737 100644 --- a/src/components/Card/Card.module.css +++ b/src/components/Card/Card.module.css @@ -114,4 +114,4 @@ .back { z-index: 2; transform: rotateY(180deg); -} +} \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index bdb9ef3e2..e856fcabf 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -23,7 +23,6 @@ function getTimerValue(startDate, endDate) { return { minutes: 0, seconds: 0, - diffInSecconds: 0, }; } @@ -32,13 +31,11 @@ function getTimerValue(startDate, endDate) { } const diffInSecconds = Math.floor((endDate.getTime() - startDate.getTime()) / 1000); - const minutes = Math.floor(diffInSecconds / 60); const seconds = diffInSecconds % 60; return { minutes, seconds, - diffInSecconds, }; } @@ -55,7 +52,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { } // Обработка количества попыток - const { tries, setTries, isEasyMode, checkedLevel, leadrs, setLeaders } = useContext(EasyContext); + const { tries, setTries, isEasyMode } = useContext(EasyContext); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); @@ -72,7 +69,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [timer, setTimer] = useState({ seconds: 0, minutes: 0, - diffInSecconds: 0, }); // Если количество попыток равно 0 устанавливается стату проиграл и игра заканчивается @@ -191,17 +187,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // ... игра продолжается }; - // Проверка на попадание в топ 10 игроков - function isTopTen() { - const isTenPlayers = leadrs.length === 10; - if (status === STATUS_WON && checkedLevel === 3) { - if (leadrs.at(-1).time > timer.diffInSecconds || (isTenPlayers && leadrs[9].time > timer.diffInSecconds)) { - return true; - } - } - return false; - } - const isGameEnded = status === STATUS_LOST || status === STATUS_WON || status === STATUS_PAUSED; // Игровой цикл @@ -228,7 +213,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { return () => { clearTimeout(timerId); }; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [status, pairsCount, previewSeconds]); // Обновляем значение таймера в интервале @@ -236,7 +220,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const intervalId = setInterval(() => { setTimer(getTimerValue(gameStartDate, gameEndDate)); }, 300); - return () => { clearInterval(intervalId); }; @@ -294,11 +277,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { gameDurationMinutes={timer.minutes} onClick={whatsNext} tries={tries} - checkedLevel={checkedLevel} - isTopTen={isTopTen()} - leadrs={leadrs} - setLeaders={setLeaders} - diffInSecconds={timer.diffInSecconds} /> ) : null} diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 07b4904ef..21bf750aa 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -86,4 +86,4 @@ flex-direction: column; align-items: flex-end; row-gap: 12px; -} +} \ No newline at end of file diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index edb824971..f5f71385d 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -1,37 +1,11 @@ import styles from "./EndGameModal.module.css"; + import { Button } from "../Button/Button"; + import deadImageUrl from "./images/dead.png"; import celebrationImageUrl from "./images/celebration.png"; -import { postLeaderboard } from "../../api/postLeaderboard"; -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; - -export function EndGameModal({ - isWon, - gameDurationSeconds, - gameDurationMinutes, - onClick, - tries, - checkedLevel, - isTopTen, - leaders, - setLeaders, - diffInSecconds, -}) { - const [inputValue, setInputValue] = useState(""); - - const navigate = useNavigate(); - function goTo() { - navigate("/Leaderboard"); - } - - function handleClick() { - postLeaderboard({ userName: inputValue, userTime: diffInSecconds }).then(leaderboard => { - setLeaders(leaderboard.leaders.sort((a, b) => a.time - b.time).slice(0, 10)); - }); - goTo(); - } +export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, onClick, tries }) { let imgSrc; let imgAlt; @@ -40,18 +14,11 @@ export function EndGameModal({ imgSrc = deadImageUrl; imgAlt = "dead emodji"; } - - // Модалка лидерборда - if (isTopTen) { - isWon = "Вы попали на лидерборд!"; - imgSrc = celebrationImageUrl; - imgAlt = "celebration emodji"; - } else if (isWon === "STATUS_WON") { + if (isWon === "STATUS_WON") { isWon = "Вы победили!"; imgSrc = celebrationImageUrl; imgAlt = "celebration emodji"; } - if (isWon === "STATUS_LOST") { isWon = "Вы проиграли!"; imgSrc = deadImageUrl; @@ -72,17 +39,8 @@ export function EndGameModal({

{title}

{isWon === "Вы допустили ошибку" &&

Оставшеся количество попыток:

} {isWon === "Вы победили!" &&

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

} - {isWon === "Вы попали на лидерборд!" && ( - setInputValue(e.target.value)} - placeholder="Введите имя" - maxLength="20" - /> - )} - {isWon === "Вы попали на лидерборд!" &&

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

} {isWon === "Вы проиграли!" &&

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

} + {isWon === "Вы допустили ошибку" && (

{tries}

@@ -94,27 +52,15 @@ export function EndGameModal({
)} - {/* Модалка лидербборда */} - {isWon === "Вы попали на лидерборд!" && ( -
- {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} -
- )} {isWon === "Вы проиграли!" && (
{gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")}
)} + {isWon === "Вы допустили ошибку" && } - {isWon === "Вы победили!" && } - {isWon === "Вы попали на лидерборд!" && } + {isWon === "Вы победили!" && } {isWon === "Вы проиграли!" && } - - {isWon === "Вы попали на лидерборд!" && ( - - )} ); -} +} \ No newline at end of file diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index b4b55c370..f250e3ed2 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,13 +1,13 @@ + .modal { width: 480px; - /* height: 459px; */ + height: 459px; border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; - padding-block: 36px; } .image { @@ -26,7 +26,6 @@ line-height: 48px; margin-bottom: 28px; - text-align: center; } .description { @@ -50,50 +49,4 @@ line-height: 72px; margin-bottom: 40px; -} - -.nameInput { - width: 276px; - height: 45px; - border-radius: 10px; - border: none; - box-sizing: border-box; - padding: 10px 14px 10px 14px; - margin-bottom: 28px; - color: #999999; - font-size: 24px; -} - -.nameInput:active, -:hover, -:focus { - outline: 0; - outline-offset: 0; -} - -.nameInput::placeholder { - color: #999999; - font-family: StratosSkyeng; - font-size: 24px; - font-style: normal; - font-weight: 400; - line-height: 32px; - text-align: center; -} - -.gameButtonsLink { - background-color: transparent; - border: none; - font-family: Roboto; - font-size: 18px; - font-weight: 400; - line-height: 32px; - text-align: center; - color: #004980; - margin-top: 18px; -} - -.gameButtonsLink:hover { - text-decoration: underline; - cursor: pointer; -} +} \ No newline at end of file diff --git a/src/contexte/contexte.jsx b/src/contexte/contexte.jsx index 14360784d..ef3efe956 100644 --- a/src/contexte/contexte.jsx +++ b/src/contexte/contexte.jsx @@ -1,34 +1,11 @@ -import { createContext, useState, useEffect } from "react"; -import { getLeaderboard } from "../api/getLeaderboard"; + +import { createContext, useState } from "react"; export const EasyContext = createContext(false); export const EasyProvider = ({ children }) => { const [tries, setTries] = useState(3); const [isEasyMode, setIsEasyMode] = useState(false); - const [leadrs, setLeaders] = useState([]); - const [checkedLevel, setCheckedLevel] = useState(); - - useEffect(() => { - if (leadrs.length === 0) { - getData(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - async function getData() { - const data = await getLeaderboard(); - const leaders = data.leaders.sort((a, b) => a.time - b.time).slice(0, 10); - setLeaders(leaders); - } - - - return ( - - {children} - - ); + return {children}; }; diff --git a/src/index.css b/src/index.css index 78f0d3a2b..f8ab8c16e 100644 --- a/src/index.css +++ b/src/index.css @@ -34,4 +34,4 @@ ol { local("Arial"); font-weight: 400; font-style: normal; -} +} \ No newline at end of file diff --git a/src/pages/GamePage/GamePage.jsx b/src/pages/GamePage/GamePage.jsx index a4be871db..2cbbd0a15 100644 --- a/src/pages/GamePage/GamePage.jsx +++ b/src/pages/GamePage/GamePage.jsx @@ -10,4 +10,4 @@ export function GamePage() { ); -} +} \ No newline at end of file diff --git a/src/pages/Leaderboard/Leaderboard.jsx b/src/pages/Leaderboard/Leaderboard.jsx deleted file mode 100644 index 3dac563c2..000000000 --- a/src/pages/Leaderboard/Leaderboard.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button } from "../../components/Button/Button"; -import styles from "../Leaderboard/Leaderboard.module.css"; -import { useNavigate } from "react-router-dom"; -import { LeaderboardPage } from "./LeaderboardPage"; -import { useContext } from "react"; -import { EasyContext } from "../../contexte/contexte"; - -export function Leaderboard() { - const { leadrs } = useContext(EasyContext); - - const navigate = useNavigate(); - function goTo() { - navigate("/"); - } - - return ( -
-
-

Лидерборд

-
- -
-
-
-

Позиция

-

Пользователь

-

Время

-
- {leadrs?.length && - leadrs.map((leader, index) => ( - - ))} -
- ); -} diff --git a/src/pages/Leaderboard/Leaderboard.module.css b/src/pages/Leaderboard/Leaderboard.module.css deleted file mode 100644 index c43287db6..000000000 --- a/src/pages/Leaderboard/Leaderboard.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.container { - max-width: 944px; - margin: 0 auto; - padding-top: 22px; - box-sizing: border-box; -} - -.header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 40px; -} - -.titleLeaderBoard { - font-family: Roboto; - color: #ffffff; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; -} - -.titleBoard { - width: 944px; - height: 64px; - background-color: #ffffff; - border-radius: 12px; - padding: 16px 20px 16px 20px; - box-sizing: border-box; - display: grid; - grid-template-columns: [start] 264px [line2] 564px [line3] 74px [end]; -} - -.title { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #999999; -} - -.titlePage { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; -} diff --git a/src/pages/Leaderboard/LeaderboardPage.jsx b/src/pages/Leaderboard/LeaderboardPage.jsx deleted file mode 100644 index ec2736f46..000000000 --- a/src/pages/Leaderboard/LeaderboardPage.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import styles from "../Leaderboard/Leaderboard.module.css"; - -export function LeaderboardPage({ id, name, time }) { - const minutes = Math.floor(time / 60); - const seconds = time % 60; - - return ( -
-
-

#{id}

-

{name.slice(0, 20)}

- -

- {minutes.toString().padStart("2", "0")}.{seconds.toString().padStart("2", "0")} -

-
-
- ); -} diff --git a/src/pages/SelectLevelPage/SelectLevelButton.jsx b/src/pages/SelectLevelPage/SelectLevelButton.jsx deleted file mode 100644 index b2c0f1c10..000000000 --- a/src/pages/SelectLevelPage/SelectLevelButton.jsx +++ /dev/null @@ -1,17 +0,0 @@ -import styles from "./SelectLevelPage.module.css"; -export function SelectLevelButton({ checkedLevel, levelNumber }) { - return ( - <> - {checkedLevel !== levelNumber && ( -
- {levelNumber} -
- )} - {checkedLevel === levelNumber && ( -
- {levelNumber} -
- )} - - ); -} diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index cf1fbd143..95801c211 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,64 +1,42 @@ -import { Link, useNavigate } from "react-router-dom"; +import { Link } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; import { useContext } from "react"; import { EasyContext } from "../../contexte/contexte"; -import { Button } from "../../components/Button/Button"; -import { SelectLevelButton } from "./SelectLevelButton"; export function SelectLevelPage() { - const { isEasyMode, setIsEasyMode, checkedLevel, setCheckedLevel } = useContext(EasyContext); - - const handleClick = id => { - setCheckedLevel(Number(id)); - }; - - const navigate = useNavigate(); - function goTo() { - if (!checkedLevel) { - alert("Необходимо выбрать уровень игры"); - } else { - navigate(`/game/${checkedLevel * 3}`); - } - } + const { isEasyMode, setIsEasyMode } = useContext(EasyContext); return (
-

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

+

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

    - - - +
  • + + 1 + +
  • +
  • + + 2 + +
  • +
  • + + 3 + +
- -
); -} +} \ No newline at end of file diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 81aa13230..59627c011 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -8,14 +8,13 @@ .modal { width: 480px; - /* height: 459px; */ + height: 459px; border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; - padding-block: 36px; } .title { @@ -47,22 +46,6 @@ border-radius: 12px; background: #fff; - cursor: pointer; -} - -.levelChek { - display: flex; - width: 97px; - height: 98px; - flex-direction: column; - justify-content: center; - flex-shrink: 0; - - border-radius: 12px; - background: #fff; - border: 4px solid #0080c1; - box-sizing: border-box; - cursor: pointer; } .levelLink { @@ -90,66 +73,19 @@ } .isEasyModeTitle { - font-family: Roboto; + color: #0080c1; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: StratosSkyeng; font-size: 24px; + font-style: normal; font-weight: 400; - line-height: 32px; - text-align: center; - cursor: pointer; -} - -.checkbox { - display: none; -} - -.customCheckbox { - position: relative; - display: inline-block; - width: 30px; - height: 30px; - background-color: #fff; - margin-right: 8px; - vertical-align: sub; - border-radius: 5px; - cursor: pointer; -} - -.customCheckbox::before { - content: ""; - display: inline-block; - width: 20px; - height: 20px; - background-image: url(/public/Vector.svg); - background-size: contain; - background-repeat: no-repeat; - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%) scale(0); - margin-top: 2px; -} - -.checkbox:checked + .customCheckbox::before { - transform: translate(-50%, -50%) scale(1); -} - -.gameButtonsContainer { + line-height: 48px; display: flex; - flex-direction: column; - align-items: center; - gap: 18px; - margin-top: 38px; } -.gameButtonsLink { - font-family: Roboto; - font-size: 18px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #004980; -} - -.radio { - display: none; -} +.checkbox { + margin-left: 16px; + width: 24px; + height: 24px; +} \ No newline at end of file diff --git a/src/router.js b/src/router.js index 4826abf63..a81f2bc1e 100644 --- a/src/router.js +++ b/src/router.js @@ -1,7 +1,6 @@ import { createBrowserRouter } from "react-router-dom"; import { GamePage } from "./pages/GamePage/GamePage"; import { SelectLevelPage } from "./pages/SelectLevelPage/SelectLevelPage"; -import { Leaderboard } from "./pages/Leaderboard/Leaderboard"; export const router = createBrowserRouter( [ @@ -13,14 +12,10 @@ export const router = createBrowserRouter( path: "/game/:pairsCount", element: , }, - { - path: "/leaderboard", - element: , - }, ], /** * basename нужен для корректной работы в gh pages * он же указан в homepage package.json и в index.html */ { basename: "/react-memo" }, -); +); \ No newline at end of file diff --git a/src/utils/cards.js b/src/utils/cards.js index 16fa6ccd9..bed790eb4 100644 --- a/src/utils/cards.js +++ b/src/utils/cards.js @@ -54,4 +54,4 @@ export function generateDeck(pairsCount = 3) { } return deck; -} +} \ No newline at end of file From 51f837eeb6397d0363b8faa1bd067c8a867c130c Mon Sep 17 00:00:00 2001 From: KharybinRuslan Date: Thu, 19 Sep 2024 00:35:15 +0300 Subject: [PATCH 4/4] New --- docs/mvp-spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mvp-spec.md b/docs/mvp-spec.md index e083d7788..8b695eb9c 100644 --- a/docs/mvp-spec.md +++ b/docs/mvp-spec.md @@ -4,7 +4,7 @@ [Ссылка на макет (страница «Инструменты разработки»).](https://www.figma.com/file/Xk8ocvZA9NlMmA0szZeI5h/%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D1%8B%D0%B9-JS?node-id=4325%3A2) -## Описание игры +## Описание игры 1 Вам предстоит реализовать следующий функционал: выбор сложности, основную логику игры, вывод результата. Ниже вы найдёте подробное описание каждого пункта.