diff --git a/src/App.css b/src/App.css index 97ffc47..9f065df 100644 --- a/src/App.css +++ b/src/App.css @@ -1,29 +1,90 @@ .app { - min-height: 100vh; + height: 100vh; + max-height: 100vh; position: relative; display: flex; flex-direction: column; align-items: center; - padding: 20px; + padding: clamp(5px, 1vh, 15px); z-index: 2; + overflow: hidden; } .app-content { width: 100%; max-width: 1600px; z-index: 2; + height: 100%; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.app-title-bar { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: clamp(5px, 1vh, 15px); + flex-shrink: 0; + gap: clamp(10px, 2vw, 20px); +} + +.app-control-buttons { + display: flex; + gap: clamp(5px, 1vw, 10px); + flex-shrink: 0; +} + +.control-button { + width: clamp(35px, 5vw, 45px); + height: clamp(35px, 5vw, 45px); + border-radius: 50%; + border: 2px solid rgba(255, 255, 255, 0.3); + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: clamp(1.2rem, 2.5vw, 1.5rem); + transition: all 0.3s ease; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + padding: 0; +} + +.control-button:hover { + transform: translateY(-2px) scale(1.1); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + border-color: rgba(255, 215, 0, 0.6); + background: rgba(255, 255, 255, 0.2); +} + +.control-button:active { + transform: translateY(0) scale(1); +} + +.control-button-players:hover { + background: rgba(78, 205, 196, 0.3); + border-color: #4ecdc4; +} + +.control-button-questions:hover { + background: rgba(102, 126, 234, 0.3); + border-color: #667eea; } .app-title { text-align: center; - margin-bottom: 20px; - font-size: 6rem; + font-size: clamp(2rem, 5vw, 4rem); font-weight: bold; text-shadow: 0 0 10px rgba(255, 215, 0, 0.8), 0 0 20px rgba(255, 215, 0, 0.6), 0 0 30px rgba(255, 215, 0, 0.4); animation: glow 2s ease-in-out infinite alternate; + flex: 1; + margin: 0; } @keyframes glow { @@ -49,28 +110,35 @@ .title-to { color: #ff6b6b; - font-size: 4.5rem; + font-size: clamp(1.5rem, 3.5vw, 3rem); +} + +.question-counter { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 2px solid rgba(255, 215, 0, 0.3); + border-radius: 20px; + padding: clamp(5px, 1vh, 8px) clamp(12px, 2vw, 18px); + color: #ffd700; + font-size: clamp(0.9rem, 2vw, 1.2rem); + font-weight: bold; + text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + flex-shrink: 0; } .app-subtitle { text-align: center; color: #fff; - font-size: 2.5rem; - margin-bottom: 40px; + font-size: clamp(1rem, 2vw, 1.5rem); + margin-bottom: clamp(5px, 1vh, 15px); text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); + flex-shrink: 0; } @media (max-width: 768px) { - .app-title { - font-size: 2.5rem; - } - - .title-to { - font-size: 2rem; - } - - .app-subtitle { - font-size: 1.2rem; + .app { + padding: clamp(3px, 0.5vh, 10px); } } diff --git a/src/App.jsx b/src/App.jsx index e532871..b471838 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,20 +1,81 @@ -import { useState } from 'react' +import { useState, useRef } from 'react' import Game from './components/Game' import Snowflakes from './components/Snowflakes' +import QuestionsModal from './components/QuestionsModal' +import { questions as initialQuestions } from './data/questions' import './App.css' function App() { + const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false) + const [questions, setQuestions] = useState(initialQuestions) + const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0) + const gameRef = useRef(null) + + const currentQuestion = questions[currentQuestionIndex] + + const handleUpdateQuestions = (updatedQuestions) => { + setQuestions(updatedQuestions) + // Если текущий вопрос был удален, сбрасываем индекс + if (currentQuestionIndex >= updatedQuestions.length) { + setCurrentQuestionIndex(0) + } + } + + const handleOpenPlayersModal = () => { + if (gameRef.current) { + gameRef.current.openPlayersModal() + } + } + return (
-

- 100 - к - 1 -

-

Новогодняя версия

- +
+
+ + +
+ +

+ 100 + к + 1 +

+ + {questions.length > 0 && currentQuestion && ( +
+ {currentQuestionIndex + 1}/{questions.length} +
+ )} +
+ + setIsQuestionsModalOpen(false)} + questions={questions} + onUpdateQuestions={handleUpdateQuestions} + /> + +
) diff --git a/src/components/Answer.css b/src/components/Answer.css index e30e0a9..87a8d4e 100644 --- a/src/components/Answer.css +++ b/src/components/Answer.css @@ -3,14 +3,15 @@ backdrop-filter: blur(10px); border: 3px solid rgba(255, 255, 255, 0.2); border-radius: 20px; - padding: 40px 30px; + padding: clamp(8px, 1.5vh, 20px) clamp(12px, 1.5vw, 20px); cursor: pointer; transition: all 0.3s ease; display: flex; flex-direction: column; align-items: center; justify-content: center; - min-height: 180px; + min-height: 0; + height: 100%; position: relative; overflow: hidden; } @@ -62,18 +63,20 @@ } .answer-placeholder { - font-size: 3rem; + font-size: clamp(1.2rem, 2.5vw, 2rem); color: rgba(255, 255, 255, 0.5); font-weight: bold; + flex-shrink: 0; } .answer-points-hidden { - font-size: 4.5rem; + font-size: clamp(1.5rem, 3vw, 2.5rem); font-weight: bold; opacity: 0.7; text-shadow: 0 0 15px currentColor; filter: blur(1px); transition: all 0.3s ease; + flex-shrink: 0; } .answer-button:hover:not(:disabled) .answer-points-hidden { @@ -83,40 +86,26 @@ } .answer-text { - font-size: 2.2rem; + font-size: clamp(0.9rem, 1.8vw, 1.4rem); color: #fff; font-weight: bold; - margin-bottom: 15px; + margin-bottom: clamp(4px, 1vh, 8px); text-align: center; text-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + flex-shrink: 1; + min-height: 0; } .answer-points { - font-size: 3.5rem; + font-size: clamp(1.2rem, 2.5vw, 2rem); font-weight: bold; text-shadow: 0 0 15px currentColor; + flex-shrink: 0; } -@media (max-width: 768px) { - .answer-button { - min-height: 80px; - padding: 20px 15px; - } - - .answer-text { - font-size: 1.1rem; - } - - .answer-points { - font-size: 1.5rem; - } - - .answer-points-hidden { - font-size: 2rem; - } - - .answer-placeholder { - font-size: 2.5rem; - } -} diff --git a/src/components/Game.css b/src/components/Game.css index 2f9eaf4..a766bb2 100644 --- a/src/components/Game.css +++ b/src/components/Game.css @@ -2,75 +2,39 @@ width: 100%; max-width: 1400px; margin: 0 auto; + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + min-height: 0; + position: relative; } .game-header { - margin-bottom: 30px; + margin-bottom: clamp(5px, 1vh, 15px); display: flex; flex-direction: column; - gap: 15px; + gap: clamp(5px, 1vh, 10px); align-items: center; + flex-shrink: 0; } -.manage-players-button { - background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%); - color: white; - border: none; - padding: 12px 30px; - font-size: 1rem; - font-weight: bold; - border-radius: 25px; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 15px rgba(78, 205, 196, 0.3); - white-space: nowrap; -} - -.manage-players-button:hover { - transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(78, 205, 196, 0.5); -} - -.manage-players-button:active { - transform: translateY(0); -} - -.game-header-buttons { +.game-content { + flex: 1; display: flex; - gap: 15px; - flex-wrap: wrap; - justify-content: center; -} - -.manage-questions-button { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - color: white; - border: none; - padding: 12px 30px; - font-size: 1rem; - font-weight: bold; - border-radius: 25px; - cursor: pointer; - transition: all 0.3s ease; - box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3); - white-space: nowrap; -} - -.manage-questions-button:hover { - transform: translateY(-2px); - box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5); -} - -.manage-questions-button:active { - transform: translateY(0); + flex-direction: column; + overflow: hidden; + min-height: 0; } .game-over { display: flex; justify-content: center; align-items: center; - min-height: 400px; - padding: 40px; + flex: 1; + padding: clamp(10px, 2vh, 20px); + overflow-y: auto; + min-height: 0; } .game-over-content { @@ -78,15 +42,17 @@ background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 20px; - padding: 60px 40px; + padding: clamp(15px, 3vh, 30px) clamp(20px, 3vw, 30px); border: 2px solid rgba(255, 215, 0, 0.3); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); + width: 100%; + max-width: 90%; } .game-over-title { - font-size: 5rem; + font-size: clamp(1.5rem, 4vw, 3rem); color: #ffd700; - margin-bottom: 30px; + margin-bottom: clamp(10px, 2vh, 20px); text-shadow: 0 0 20px rgba(255, 215, 0, 0.8); } @@ -101,8 +67,8 @@ background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%); color: white; border: none; - padding: 25px 60px; - font-size: 2rem; + padding: clamp(10px, 2vh, 15px) clamp(30px, 5vw, 50px); + font-size: clamp(1rem, 2vw, 1.5rem); border-radius: 35px; cursor: pointer; font-weight: bold; @@ -121,29 +87,36 @@ .no-players-message { text-align: center; - padding: 60px 20px; + padding: clamp(20px, 4vh, 40px) clamp(15px, 2vw, 20px); background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 20px; border: 2px solid rgba(255, 215, 0, 0.3); + flex: 1; + display: flex; + align-items: center; + justify-content: center; + min-height: 0; } .no-players-message p { color: #fff; - font-size: 1.5rem; + font-size: clamp(1rem, 2vw, 1.3rem); margin: 0; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); } .final-scores { width: 100%; - margin: 30px 0; + margin: clamp(10px, 2vh, 20px) 0; + max-height: 40vh; + overflow-y: auto; } .final-scores-title { color: #fff; - font-size: 2rem; - margin-bottom: 20px; + font-size: clamp(1.2rem, 2.5vw, 1.8rem); + margin-bottom: clamp(10px, 2vh, 15px); text-align: center; } @@ -151,8 +124,8 @@ display: flex; justify-content: space-between; align-items: center; - padding: 15px 25px; - margin: 10px 0; + padding: clamp(8px, 1.5vh, 12px) clamp(15px, 2vw, 20px); + margin: clamp(5px, 1vh, 8px) 0; background: rgba(255, 255, 255, 0.1); border-radius: 12px; border: 2px solid rgba(255, 255, 255, 0.2); @@ -167,13 +140,13 @@ .final-score-name { color: #fff; - font-size: 1.5rem; + font-size: clamp(1rem, 2vw, 1.3rem); font-weight: 500; } .final-score-value { color: #ffd700; - font-size: 1.5rem; + font-size: clamp(1rem, 2vw, 1.3rem); font-weight: bold; text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); } @@ -184,50 +157,4 @@ text-shadow: 0 0 15px rgba(255, 215, 0, 0.8); } -@media (max-width: 768px) { - .game-over-title { - font-size: 2rem; - } - - .game-over-score { - font-size: 1.5rem; - } - - .restart-button { - font-size: 1.1rem; - padding: 12px 30px; - } - - .no-players-message p { - font-size: 1.2rem; - } - - .final-scores-title { - font-size: 1.5rem; - } - - .final-score-item { - padding: 12px 20px; - } - - .final-score-name, - .final-score-value { - font-size: 1.2rem; - } - - .manage-players-button, - .manage-questions-button { - font-size: 0.9rem; - padding: 10px 20px; - } - - .game-header-buttons { - flex-direction: column; - width: 100%; - } - - .game-header { - margin-bottom: 20px; - } -} diff --git a/src/components/Game.jsx b/src/components/Game.jsx index 0c11a72..a12b081 100644 --- a/src/components/Game.jsx +++ b/src/components/Game.jsx @@ -1,23 +1,29 @@ -import { useState } from 'react' +import { useState, useImperativeHandle, forwardRef } from 'react' import Question from './Question' import Players from './Players' import PlayersModal from './PlayersModal' import QuestionsModal from './QuestionsModal' -import Score from './Score' -import { questions as initialQuestions } from '../data/questions' import './Game.css' -const Game = () => { +const Game = forwardRef(({ + questions = [], + currentQuestionIndex = 0, + onQuestionIndexChange, + onQuestionsChange, +}, ref) => { const [players, setPlayers] = useState([]) const [currentPlayerId, setCurrentPlayerId] = useState(null) const [playerScores, setPlayerScores] = useState({}) - const [questions, setQuestions] = useState(initialQuestions) - const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0) const [gameOver, setGameOver] = useState(false) const [revealedAnswers, setRevealedAnswers] = useState([]) const [isPlayersModalOpen, setIsPlayersModalOpen] = useState(false) const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false) + useImperativeHandle(ref, () => ({ + openPlayersModal: () => setIsPlayersModalOpen(true), + openQuestionsModal: () => setIsQuestionsModalOpen(true), + })) + const currentQuestion = questions[currentQuestionIndex] const isLastQuestion = currentQuestionIndex === questions.length - 1 @@ -112,12 +118,16 @@ const Game = () => { } const nextQuestion = () => { - setCurrentQuestionIndex(currentQuestionIndex + 1) + if (onQuestionIndexChange) { + onQuestionIndexChange(currentQuestionIndex + 1) + } setRevealedAnswers([]) } const restartGame = () => { - setCurrentQuestionIndex(0) + if (onQuestionIndexChange) { + onQuestionIndexChange(0) + } setGameOver(false) setRevealedAnswers([]) const initialScores = {} @@ -130,15 +140,6 @@ const Game = () => { } } - const handleUpdateQuestions = (updatedQuestions) => { - setQuestions(updatedQuestions) - // Если текущий вопрос был удален, сбрасываем индекс - if (currentQuestionIndex >= updatedQuestions.length) { - setCurrentQuestionIndex(0) - setRevealedAnswers([]) - } - } - if (gameOver) { // Находим победителя(ей) const scores = Object.values(playerScores) @@ -189,25 +190,10 @@ const Game = () => { isOpen={isQuestionsModalOpen} onClose={() => setIsQuestionsModalOpen(false)} questions={questions} - onUpdateQuestions={handleUpdateQuestions} + onUpdateQuestions={onQuestionsChange} />
-
- - -
- {players.length > 0 && ( {
{players.length > 0 && currentPlayerId ? ( - <> +
{questions.length === 0 ? (

Добавьте вопросы, чтобы начать игру

) : currentQuestion ? ( - <> - - - + ) : (

Ошибка: вопрос не найден

)} - +
) : (

Добавьте участников, чтобы начать игру

@@ -251,7 +230,9 @@ const Game = () => { )}
) -} +}) + +Game.displayName = 'Game' export default Game diff --git a/src/components/Players.css b/src/components/Players.css index 2ece3c3..835e55f 100644 --- a/src/components/Players.css +++ b/src/components/Players.css @@ -1,5 +1,6 @@ .players-container { - margin-bottom: 20px; + margin-bottom: clamp(5px, 1vh, 10px); + flex-shrink: 0; } .players-list { diff --git a/src/components/PlayersModal.css b/src/components/PlayersModal.css index bf374b1..a43fea8 100644 --- a/src/components/PlayersModal.css +++ b/src/components/PlayersModal.css @@ -17,10 +17,10 @@ background: rgba(20, 20, 30, 0.95); backdrop-filter: blur(20px); border-radius: 20px; - padding: 30px; + padding: clamp(15px, 3vh, 25px); max-width: 500px; width: 100%; - max-height: 80vh; + max-height: 90vh; overflow-y: auto; border: 2px solid rgba(255, 215, 0, 0.3); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); @@ -31,12 +31,12 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 25px; + margin-bottom: clamp(15px, 2vh, 20px); } .players-modal-title { color: #ffd700; - font-size: 2rem; + font-size: clamp(1.3rem, 2.5vw, 1.8rem); margin: 0; text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); } diff --git a/src/components/Question.css b/src/components/Question.css index 00f286e..5b58a95 100644 --- a/src/components/Question.css +++ b/src/components/Question.css @@ -1,16 +1,22 @@ .question-container { width: 100%; + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + min-height: 0; } .question-box { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 25px; - padding: 50px; - margin-bottom: 40px; + padding: clamp(15px, 3vh, 30px); + margin-bottom: clamp(5px, 1vh, 15px); border: 3px solid rgba(255, 215, 0, 0.3); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); text-align: center; + flex-shrink: 0; } .question-number { @@ -18,9 +24,9 @@ background: rgba(255, 215, 0, 0.2); border: 2px solid rgba(255, 215, 0, 0.5); border-radius: 15px; - padding: 12px 30px; - margin-bottom: 25px; - font-size: 1.8rem; + padding: clamp(6px, 1vh, 10px) clamp(15px, 2vw, 25px); + margin-bottom: clamp(10px, 2vh, 15px); + font-size: clamp(1rem, 2vw, 1.4rem); color: #ffd700; font-weight: bold; text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); @@ -29,29 +35,23 @@ .question-text { color: #fff; - font-size: 3.5rem; + font-size: clamp(1.2rem, 3vw, 2.5rem); font-weight: bold; - line-height: 1.4; + line-height: 1.3; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); } .answers-grid { display: grid; grid-template-columns: repeat(2, 1fr); - gap: 30px; + grid-auto-rows: minmax(0, 1fr); + gap: clamp(10px, 2vw, 20px); + flex: 1; + min-height: 0; + overflow: hidden; } @media (max-width: 768px) { - .question-number { - font-size: 1.3rem; - padding: 10px 20px; - margin-bottom: 20px; - } - - .question-text { - font-size: 1.5rem; - } - .answers-grid { grid-template-columns: 1fr; } diff --git a/src/components/Question.jsx b/src/components/Question.jsx index 97ebf07..cad5675 100644 --- a/src/components/Question.jsx +++ b/src/components/Question.jsx @@ -5,7 +5,6 @@ const Question = ({ question, questionNumber, onAnswerClick, revealedAnswers }) return (
-
Вопрос {questionNumber}

{question.text}

diff --git a/src/components/QuestionsModal.css b/src/components/QuestionsModal.css index a01e44f..70c73ce 100644 --- a/src/components/QuestionsModal.css +++ b/src/components/QuestionsModal.css @@ -17,7 +17,7 @@ background: rgba(20, 20, 30, 0.95); backdrop-filter: blur(20px); border-radius: 20px; - padding: 30px; + padding: clamp(15px, 3vh, 25px); max-width: 800px; width: 100%; max-height: 90vh; @@ -31,12 +31,12 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 25px; + margin-bottom: clamp(15px, 2vh, 20px); } .questions-modal-title { color: #ffd700; - font-size: 2rem; + font-size: clamp(1.3rem, 2.5vw, 1.8rem); margin: 0; text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); } diff --git a/src/components/Score.css b/src/components/Score.css deleted file mode 100644 index 50307d8..0000000 --- a/src/components/Score.css +++ /dev/null @@ -1,45 +0,0 @@ -.score-container { - display: flex; - justify-content: space-around; - margin-bottom: 40px; - gap: 30px; -} - -.score-item { - flex: 1; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - border-radius: 20px; - padding: 35px; - text-align: center; - border: 3px solid rgba(255, 215, 0, 0.3); - box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); -} - -.score-label { - display: block; - color: rgba(255, 255, 255, 0.8); - font-size: 1.8rem; - margin-bottom: 15px; - text-transform: uppercase; - letter-spacing: 2px; -} - -.score-value { - display: block; - color: #ffd700; - font-size: 4.5rem; - font-weight: bold; - text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); -} - -@media (max-width: 768px) { - .score-container { - flex-direction: column; - } - - .score-value { - font-size: 2rem; - } -} - diff --git a/src/components/Score.jsx b/src/components/Score.jsx deleted file mode 100644 index 4c8f110..0000000 --- a/src/components/Score.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import './Score.css' - -const Score = ({ score, questionNumber, totalQuestions }) => { - return ( -
-
- Счёт: - {score} -
-
- Вопрос: - - {questionNumber}/{totalQuestions} - -
-
- ) -} - -export default Score - diff --git a/src/index.css b/src/index.css index 29ac3ed..0cafd9b 100644 --- a/src/index.css +++ b/src/index.css @@ -12,11 +12,18 @@ body { -moz-osx-font-smoothing: grayscale; background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%); min-height: 100vh; - overflow-x: hidden; + max-height: 100vh; + overflow: hidden; + margin: 0; + padding: 0; } #root { min-height: 100vh; + max-height: 100vh; + overflow: hidden; + display: flex; + flex-direction: column; } /* Новогодние снежинки */