ui fixes
This commit is contained in:
parent
6a94a76c19
commit
ae52913209
7 changed files with 393 additions and 23 deletions
39
src/App.css
39
src/App.css
|
|
@ -74,6 +74,11 @@
|
||||||
border-color: #667eea;
|
border-color: #667eea;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.control-button-new-game:hover {
|
||||||
|
background: rgba(255, 107, 107, 0.3);
|
||||||
|
border-color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
.app-title {
|
.app-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: clamp(2rem, 5vw, 4rem);
|
font-size: clamp(2rem, 5vw, 4rem);
|
||||||
|
|
@ -113,6 +118,13 @@
|
||||||
font-size: clamp(1.5rem, 3.5vw, 3rem);
|
font-size: clamp(1.5rem, 3.5vw, 3rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.question-counter-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: clamp(8px, 1.5vw, 12px);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.question-counter {
|
.question-counter {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
|
@ -124,7 +136,32 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
|
text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
|
||||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
flex-shrink: 0;
|
}
|
||||||
|
|
||||||
|
.show-all-button-top {
|
||||||
|
background: rgba(255, 107, 107, 0.2);
|
||||||
|
color: #ff6b6b;
|
||||||
|
border: 2px solid rgba(255, 107, 107, 0.5);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: clamp(5px, 1vh, 8px) clamp(12px, 2vw, 18px);
|
||||||
|
font-size: clamp(0.8rem, 1.5vw, 1rem);
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 107, 107, 0.5);
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-all-button-top:hover {
|
||||||
|
background: rgba(255, 107, 107, 0.4);
|
||||||
|
border-color: #ff6b6b;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-all-button-top:active {
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-subtitle {
|
.app-subtitle {
|
||||||
|
|
|
||||||
70
src/App.jsx
70
src/App.jsx
|
|
@ -1,18 +1,37 @@
|
||||||
import { useState, useRef } from 'react'
|
import { useState, useRef, useEffect } from 'react'
|
||||||
import Game from './components/Game'
|
import Game from './components/Game'
|
||||||
import Snowflakes from './components/Snowflakes'
|
import Snowflakes from './components/Snowflakes'
|
||||||
import QuestionsModal from './components/QuestionsModal'
|
import QuestionsModal from './components/QuestionsModal'
|
||||||
import { questions as initialQuestions } from './data/questions'
|
import { questions as initialQuestions } from './data/questions'
|
||||||
|
import { getCookie, setCookie, deleteCookie } from './utils/cookies'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false)
|
const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false)
|
||||||
const [questions, setQuestions] = useState(initialQuestions)
|
const [questions, setQuestions] = useState(() => {
|
||||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
|
const savedQuestions = getCookie('gameQuestions')
|
||||||
|
return savedQuestions || initialQuestions
|
||||||
|
})
|
||||||
|
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(() => {
|
||||||
|
const savedIndex = getCookie('gameQuestionIndex')
|
||||||
|
return savedIndex !== null ? savedIndex : 0
|
||||||
|
})
|
||||||
const gameRef = useRef(null)
|
const gameRef = useRef(null)
|
||||||
|
|
||||||
const currentQuestion = questions[currentQuestionIndex]
|
const currentQuestion = questions[currentQuestionIndex]
|
||||||
|
|
||||||
|
// Сохраняем вопросы в cookies при изменении
|
||||||
|
useEffect(() => {
|
||||||
|
if (questions.length > 0) {
|
||||||
|
setCookie('gameQuestions', questions)
|
||||||
|
}
|
||||||
|
}, [questions])
|
||||||
|
|
||||||
|
// Сохраняем индекс вопроса в cookies при изменении
|
||||||
|
useEffect(() => {
|
||||||
|
setCookie('gameQuestionIndex', currentQuestionIndex)
|
||||||
|
}, [currentQuestionIndex])
|
||||||
|
|
||||||
const handleUpdateQuestions = (updatedQuestions) => {
|
const handleUpdateQuestions = (updatedQuestions) => {
|
||||||
setQuestions(updatedQuestions)
|
setQuestions(updatedQuestions)
|
||||||
// Если текущий вопрос был удален, сбрасываем индекс
|
// Если текущий вопрос был удален, сбрасываем индекс
|
||||||
|
|
@ -27,6 +46,31 @@ function App() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleNewGame = () => {
|
||||||
|
if (window.confirm('Начать новую игру? Текущий прогресс будет потерян.')) {
|
||||||
|
deleteCookie('gameQuestions')
|
||||||
|
deleteCookie('gameQuestionIndex')
|
||||||
|
deleteCookie('gamePlayers')
|
||||||
|
deleteCookie('gamePlayerScores')
|
||||||
|
deleteCookie('gameCurrentPlayerId')
|
||||||
|
deleteCookie('gameRevealedAnswers')
|
||||||
|
deleteCookie('gameOver')
|
||||||
|
|
||||||
|
setQuestions(initialQuestions)
|
||||||
|
setCurrentQuestionIndex(0)
|
||||||
|
|
||||||
|
if (gameRef.current) {
|
||||||
|
gameRef.current.newGame()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleShowAll = () => {
|
||||||
|
if (gameRef.current && gameRef.current.showAllAnswers) {
|
||||||
|
gameRef.current.showAllAnswers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<Snowflakes />
|
<Snowflakes />
|
||||||
|
|
@ -47,6 +91,13 @@ function App() {
|
||||||
>
|
>
|
||||||
❓
|
❓
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="control-button control-button-new-game"
|
||||||
|
onClick={handleNewGame}
|
||||||
|
title="Новая игра"
|
||||||
|
>
|
||||||
|
🎮
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 className="app-title">
|
<h1 className="app-title">
|
||||||
|
|
@ -56,8 +107,17 @@ function App() {
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{questions.length > 0 && currentQuestion && (
|
{questions.length > 0 && currentQuestion && (
|
||||||
<div className="question-counter">
|
<div className="question-counter-wrapper">
|
||||||
{currentQuestionIndex + 1}/{questions.length}
|
<div className="question-counter">
|
||||||
|
{currentQuestionIndex + 1}/{questions.length}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="show-all-button-top"
|
||||||
|
onClick={handleShowAll}
|
||||||
|
title="Показать все ответы"
|
||||||
|
>
|
||||||
|
Показать все
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,16 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Горизонтальный layout для узких кнопок */
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.answer-button {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: clamp(8px, 1.5vw, 15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.answer-button:hover:not(:disabled) {
|
.answer-button:hover:not(:disabled) {
|
||||||
transform: translateY(-5px);
|
transform: translateY(-5px);
|
||||||
box-shadow: 0 8px 25px rgba(255, 215, 0, 0.4);
|
box-shadow: 0 8px 25px rgba(255, 215, 0, 0.4);
|
||||||
|
|
@ -99,6 +109,18 @@
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.answer-text {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: clamp(8px, 1.5vw, 15px);
|
||||||
|
text-align: left;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-points {
|
.answer-points {
|
||||||
|
|
@ -108,4 +130,11 @@
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.answer-points {
|
||||||
|
font-size: clamp(1.5rem, 3vw, 2.5rem);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useState, useImperativeHandle, forwardRef } from 'react'
|
import { useState, useImperativeHandle, forwardRef, useEffect } from 'react'
|
||||||
import Question from './Question'
|
import Question from './Question'
|
||||||
import Players from './Players'
|
import Players from './Players'
|
||||||
import PlayersModal from './PlayersModal'
|
import PlayersModal from './PlayersModal'
|
||||||
import QuestionsModal from './QuestionsModal'
|
import QuestionsModal from './QuestionsModal'
|
||||||
|
import { getCookie, setCookie, deleteCookie } from '../utils/cookies'
|
||||||
import './Game.css'
|
import './Game.css'
|
||||||
|
|
||||||
const Game = forwardRef(({
|
const Game = forwardRef(({
|
||||||
|
|
@ -11,22 +12,97 @@ const Game = forwardRef(({
|
||||||
onQuestionIndexChange,
|
onQuestionIndexChange,
|
||||||
onQuestionsChange,
|
onQuestionsChange,
|
||||||
}, ref) => {
|
}, ref) => {
|
||||||
const [players, setPlayers] = useState([])
|
const [players, setPlayers] = useState(() => {
|
||||||
const [currentPlayerId, setCurrentPlayerId] = useState(null)
|
const savedPlayers = getCookie('gamePlayers')
|
||||||
const [playerScores, setPlayerScores] = useState({})
|
return savedPlayers || []
|
||||||
const [gameOver, setGameOver] = useState(false)
|
})
|
||||||
const [revealedAnswers, setRevealedAnswers] = useState([])
|
const [currentPlayerId, setCurrentPlayerId] = useState(() => {
|
||||||
|
const savedId = getCookie('gameCurrentPlayerId')
|
||||||
|
return savedId !== null ? savedId : null
|
||||||
|
})
|
||||||
|
const [playerScores, setPlayerScores] = useState(() => {
|
||||||
|
const savedScores = getCookie('gamePlayerScores')
|
||||||
|
return savedScores || {}
|
||||||
|
})
|
||||||
|
const [gameOver, setGameOver] = useState(() => {
|
||||||
|
const savedGameOver = getCookie('gameOver')
|
||||||
|
return savedGameOver === true
|
||||||
|
})
|
||||||
|
const [revealedAnswers, setRevealedAnswers] = useState(() => {
|
||||||
|
const savedAnswers = getCookie('gameRevealedAnswers')
|
||||||
|
return savedAnswers || {}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Получаем открытые ответы для текущего вопроса
|
||||||
|
const getCurrentRevealedAnswers = () => {
|
||||||
|
return revealedAnswers[currentQuestionIndex] || []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем открытые ответы для текущего вопроса
|
||||||
|
const updateRevealedAnswers = (newAnswers) => {
|
||||||
|
setRevealedAnswers({
|
||||||
|
...revealedAnswers,
|
||||||
|
[currentQuestionIndex]: newAnswers,
|
||||||
|
})
|
||||||
|
}
|
||||||
const [isPlayersModalOpen, setIsPlayersModalOpen] = useState(false)
|
const [isPlayersModalOpen, setIsPlayersModalOpen] = useState(false)
|
||||||
const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false)
|
const [isQuestionsModalOpen, setIsQuestionsModalOpen] = useState(false)
|
||||||
|
|
||||||
|
// Сохраняем состояние в cookies при изменении
|
||||||
|
useEffect(() => {
|
||||||
|
if (players.length > 0) {
|
||||||
|
setCookie('gamePlayers', players)
|
||||||
|
} else {
|
||||||
|
deleteCookie('gamePlayers')
|
||||||
|
}
|
||||||
|
}, [players])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentPlayerId !== null) {
|
||||||
|
setCookie('gameCurrentPlayerId', currentPlayerId)
|
||||||
|
} else {
|
||||||
|
deleteCookie('gameCurrentPlayerId')
|
||||||
|
}
|
||||||
|
}, [currentPlayerId])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Object.keys(playerScores).length > 0) {
|
||||||
|
setCookie('gamePlayerScores', playerScores)
|
||||||
|
} else {
|
||||||
|
deleteCookie('gamePlayerScores')
|
||||||
|
}
|
||||||
|
}, [playerScores])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCookie('gameRevealedAnswers', revealedAnswers)
|
||||||
|
}, [revealedAnswers])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCookie('gameOver', gameOver)
|
||||||
|
}, [gameOver])
|
||||||
|
|
||||||
|
const currentQuestion = questions[currentQuestionIndex]
|
||||||
|
const isLastQuestion = currentQuestionIndex === questions.length - 1
|
||||||
|
|
||||||
|
const handleShowAllAnswers = () => {
|
||||||
|
if (!currentQuestion) return
|
||||||
|
const allAnswerIndices = currentQuestion.answers.map((_, index) => index)
|
||||||
|
updateRevealedAnswers(allAnswerIndices)
|
||||||
|
}
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
openPlayersModal: () => setIsPlayersModalOpen(true),
|
openPlayersModal: () => setIsPlayersModalOpen(true),
|
||||||
openQuestionsModal: () => setIsQuestionsModalOpen(true),
|
openQuestionsModal: () => setIsQuestionsModalOpen(true),
|
||||||
|
newGame: () => {
|
||||||
|
setPlayers([])
|
||||||
|
setCurrentPlayerId(null)
|
||||||
|
setPlayerScores({})
|
||||||
|
setGameOver(false)
|
||||||
|
setRevealedAnswers({})
|
||||||
|
},
|
||||||
|
showAllAnswers: handleShowAllAnswers,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const currentQuestion = questions[currentQuestionIndex]
|
|
||||||
const isLastQuestion = currentQuestionIndex === questions.length - 1
|
|
||||||
|
|
||||||
const handleAddPlayer = (name) => {
|
const handleAddPlayer = (name) => {
|
||||||
const newPlayer = {
|
const newPlayer = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
|
|
@ -76,13 +152,14 @@ const Game = forwardRef(({
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAnswerClick = (answerIndex, points) => {
|
const handleAnswerClick = (answerIndex, points) => {
|
||||||
if (revealedAnswers.includes(answerIndex)) return
|
const currentRevealed = getCurrentRevealedAnswers()
|
||||||
|
if (currentRevealed.includes(answerIndex)) return
|
||||||
if (!currentPlayerId) return
|
if (!currentPlayerId) return
|
||||||
if (!currentQuestion) return
|
if (!currentQuestion) return
|
||||||
|
|
||||||
const isLastAnswer = revealedAnswers.length === currentQuestion.answers.length - 1
|
const isLastAnswer = currentRevealed.length === currentQuestion.answers.length - 1
|
||||||
|
|
||||||
setRevealedAnswers([...revealedAnswers, answerIndex])
|
updateRevealedAnswers([...currentRevealed, answerIndex])
|
||||||
|
|
||||||
// Добавляем очки текущему участнику
|
// Добавляем очки текущему участнику
|
||||||
setPlayerScores({
|
setPlayerScores({
|
||||||
|
|
@ -121,7 +198,7 @@ const Game = forwardRef(({
|
||||||
if (onQuestionIndexChange) {
|
if (onQuestionIndexChange) {
|
||||||
onQuestionIndexChange(currentQuestionIndex + 1)
|
onQuestionIndexChange(currentQuestionIndex + 1)
|
||||||
}
|
}
|
||||||
setRevealedAnswers([])
|
// Не сбрасываем открытые ответы - они сохраняются для каждого вопроса отдельно
|
||||||
}
|
}
|
||||||
|
|
||||||
const restartGame = () => {
|
const restartGame = () => {
|
||||||
|
|
@ -129,7 +206,7 @@ const Game = forwardRef(({
|
||||||
onQuestionIndexChange(0)
|
onQuestionIndexChange(0)
|
||||||
}
|
}
|
||||||
setGameOver(false)
|
setGameOver(false)
|
||||||
setRevealedAnswers([])
|
setRevealedAnswers({})
|
||||||
const initialScores = {}
|
const initialScores = {}
|
||||||
players.forEach(player => {
|
players.forEach(player => {
|
||||||
initialScores[player.id] = 0
|
initialScores[player.id] = 0
|
||||||
|
|
@ -140,6 +217,31 @@ const Game = forwardRef(({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newGame = () => {
|
||||||
|
setPlayers([])
|
||||||
|
setCurrentPlayerId(null)
|
||||||
|
setPlayerScores({})
|
||||||
|
setGameOver(false)
|
||||||
|
setRevealedAnswers({})
|
||||||
|
if (onQuestionIndexChange) {
|
||||||
|
onQuestionIndexChange(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreviousQuestion = () => {
|
||||||
|
if (currentQuestionIndex > 0 && onQuestionIndexChange) {
|
||||||
|
onQuestionIndexChange(currentQuestionIndex - 1)
|
||||||
|
// Открытые ответы сохраняются для каждого вопроса отдельно
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleNextQuestion = () => {
|
||||||
|
if (currentQuestionIndex < questions.length - 1 && onQuestionIndexChange) {
|
||||||
|
onQuestionIndexChange(currentQuestionIndex + 1)
|
||||||
|
// Открытые ответы сохраняются для каждого вопроса отдельно
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
// Находим победителя(ей)
|
// Находим победителя(ей)
|
||||||
const scores = Object.values(playerScores)
|
const scores = Object.values(playerScores)
|
||||||
|
|
@ -215,7 +317,11 @@ const Game = forwardRef(({
|
||||||
question={currentQuestion}
|
question={currentQuestion}
|
||||||
questionNumber={currentQuestionIndex + 1}
|
questionNumber={currentQuestionIndex + 1}
|
||||||
onAnswerClick={handleAnswerClick}
|
onAnswerClick={handleAnswerClick}
|
||||||
revealedAnswers={revealedAnswers}
|
revealedAnswers={getCurrentRevealedAnswers()}
|
||||||
|
onPreviousQuestion={handlePreviousQuestion}
|
||||||
|
onNextQuestion={handleNextQuestion}
|
||||||
|
canGoPrevious={currentQuestionIndex > 0}
|
||||||
|
canGoNext={currentQuestionIndex < questions.length - 1}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="no-players-message">
|
<div className="no-players-message">
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,54 @@
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.question-navigation {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: clamp(10px, 2vw, 20px);
|
||||||
|
margin-bottom: clamp(10px, 2vh, 15px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-nav-button {
|
||||||
|
width: clamp(40px, 6vw, 50px);
|
||||||
|
height: clamp(40px, 6vw, 50px);
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
color: #fff;
|
||||||
|
font-size: clamp(1.5rem, 3vw, 2rem);
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-nav-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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-nav-button:active {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-nav-button-prev:hover {
|
||||||
|
background: rgba(78, 205, 196, 0.3);
|
||||||
|
border-color: #4ecdc4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.question-nav-button-next:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.3);
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
.question-number {
|
.question-number {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background: rgba(255, 215, 0, 0.2);
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
|
@ -39,6 +87,33 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
||||||
|
margin: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-all-button {
|
||||||
|
background: rgba(255, 107, 107, 0.2);
|
||||||
|
color: #ff6b6b;
|
||||||
|
border: 2px solid rgba(255, 107, 107, 0.5);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: clamp(8px, 1.5vh, 12px) clamp(20px, 3vw, 30px);
|
||||||
|
font-size: clamp(0.9rem, 1.8vw, 1.2rem);
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 107, 107, 0.5);
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-all-button:hover {
|
||||||
|
background: rgba(255, 107, 107, 0.4);
|
||||||
|
border-color: #ff6b6b;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-all-button:active {
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.answers-grid {
|
.answers-grid {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,44 @@
|
||||||
import Answer from './Answer'
|
import Answer from './Answer'
|
||||||
import './Question.css'
|
import './Question.css'
|
||||||
|
|
||||||
const Question = ({ question, questionNumber, onAnswerClick, revealedAnswers }) => {
|
const Question = ({
|
||||||
|
question,
|
||||||
|
questionNumber,
|
||||||
|
onAnswerClick,
|
||||||
|
revealedAnswers,
|
||||||
|
onShowAll,
|
||||||
|
onPreviousQuestion,
|
||||||
|
onNextQuestion,
|
||||||
|
canGoPrevious,
|
||||||
|
canGoNext,
|
||||||
|
}) => {
|
||||||
|
const allAnswersRevealed = question.answers.every((_, index) => revealedAnswers.includes(index))
|
||||||
|
const hasUnrevealedAnswers = revealedAnswers.length < question.answers.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="question-container">
|
<div className="question-container">
|
||||||
<div className="question-box">
|
<div className="question-box">
|
||||||
<h2 className="question-text">{question.text}</h2>
|
<div className="question-navigation">
|
||||||
|
{canGoPrevious && onPreviousQuestion && (
|
||||||
|
<button
|
||||||
|
className="question-nav-button question-nav-button-prev"
|
||||||
|
onClick={onPreviousQuestion}
|
||||||
|
title="Предыдущий вопрос"
|
||||||
|
>
|
||||||
|
←
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<h2 className="question-text">{question.text}</h2>
|
||||||
|
{canGoNext && onNextQuestion && (
|
||||||
|
<button
|
||||||
|
className="question-nav-button question-nav-button-next"
|
||||||
|
onClick={onNextQuestion}
|
||||||
|
title="Следующий вопрос"
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="answers-grid">
|
<div className="answers-grid">
|
||||||
{question.answers.map((answer, index) => (
|
{question.answers.map((answer, index) => (
|
||||||
|
|
|
||||||
30
src/utils/cookies.js
Normal file
30
src/utils/cookies.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Утилиты для работы с cookies
|
||||||
|
|
||||||
|
export const setCookie = (name, value, days = 365) => {
|
||||||
|
const date = new Date()
|
||||||
|
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)
|
||||||
|
const expires = `expires=${date.toUTCString()}`
|
||||||
|
document.cookie = `${name}=${encodeURIComponent(JSON.stringify(value))};${expires};path=/`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCookie = (name) => {
|
||||||
|
const nameEQ = `${name}=`
|
||||||
|
const ca = document.cookie.split(';')
|
||||||
|
for (let i = 0; i < ca.length; i++) {
|
||||||
|
let c = ca[i]
|
||||||
|
while (c.charAt(0) === ' ') c = c.substring(1, c.length)
|
||||||
|
if (c.indexOf(nameEQ) === 0) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(decodeURIComponent(c.substring(nameEQ.length, c.length)))
|
||||||
|
} catch (e) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteCookie = (name) => {
|
||||||
|
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;`
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in a new issue