@mblinkov/whats-missing 20.0.49 → 20.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Game.d.ts +5 -0
- package/dist/Game.styles.d.ts +2 -0
- package/dist/index.d.ts +3 -0
- package/dist/themes.d.ts +6 -0
- package/dist/whats-missing.cjs.js +3 -3
- package/dist/whats-missing.es.js +124 -122
- package/package.json +24 -3
- package/.prettierrc +0 -9
- package/src/Game.styles.ts +0 -221
- package/src/Game.tsx +0 -1185
- package/src/index.ts +0 -4
- package/src/themes.ts +0 -96
- package/tsconfig.json +0 -19
- package/vite.config.ts +0 -34
package/src/Game.tsx
DELETED
|
@@ -1,1185 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useMemo, useRef } from "react";
|
|
2
|
-
import { themes } from "./themes";
|
|
3
|
-
import { styles } from "./Game.styles";
|
|
4
|
-
import type { Theme } from "./themes";
|
|
5
|
-
|
|
6
|
-
type ImageItem = { src: string; name: string };
|
|
7
|
-
type RoundResult = {
|
|
8
|
-
round: number;
|
|
9
|
-
answer: string;
|
|
10
|
-
correct: string;
|
|
11
|
-
result: "correct" | "almost" | "wrong";
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// ✅ базовый reset
|
|
15
|
-
const globalReset = () => {
|
|
16
|
-
const style = document.createElement("style");
|
|
17
|
-
style.id = "whats-missing-reset"; // ✅ Добавляем ID для удаления
|
|
18
|
-
|
|
19
|
-
style.textContent = `
|
|
20
|
-
#whats-missing-root, #whats-missing-root * {
|
|
21
|
-
box-sizing: border-box;
|
|
22
|
-
font-family: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
|
|
23
|
-
}
|
|
24
|
-
#whats-missing-root img {
|
|
25
|
-
max-width: 100%;
|
|
26
|
-
height: auto;
|
|
27
|
-
display: block;
|
|
28
|
-
user-select: none;
|
|
29
|
-
}
|
|
30
|
-
html, body {
|
|
31
|
-
margin: 0 !important;
|
|
32
|
-
padding: 0 !important;
|
|
33
|
-
width: 100% !important;
|
|
34
|
-
height: 100% !important;
|
|
35
|
-
overflow: hidden !important;
|
|
36
|
-
zoom: 1 !important; /* ✅ защита от подзума */
|
|
37
|
-
}
|
|
38
|
-
#root {
|
|
39
|
-
margin: 0 !important;
|
|
40
|
-
padding: 0 !important;
|
|
41
|
-
width: 100% !important;
|
|
42
|
-
height: 100% !important;
|
|
43
|
-
overflow: hidden !important;
|
|
44
|
-
}
|
|
45
|
-
`;
|
|
46
|
-
|
|
47
|
-
// ✅ Удаляем старый стиль, если он есть
|
|
48
|
-
const existingStyle = document.getElementById("whats-missing-reset");
|
|
49
|
-
if (existingStyle) {
|
|
50
|
-
existingStyle.remove();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
document.head.appendChild(style);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// простая функция сравнения
|
|
57
|
-
const levenshtein = (a: string, b: string) => {
|
|
58
|
-
const dp = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
|
|
59
|
-
for (let i = 0; i <= a.length; i++) dp[i][0] = i;
|
|
60
|
-
for (let j = 0; j <= b.length; j++) dp[0][j] = j;
|
|
61
|
-
for (let i = 1; i <= a.length; i++) {
|
|
62
|
-
for (let j = 1; j <= b.length; j++) {
|
|
63
|
-
dp[i][j] =
|
|
64
|
-
a[i - 1] === b[j - 1]
|
|
65
|
-
? dp[i - 1][j - 1]
|
|
66
|
-
: Math.min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]) + 1;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return dp[a.length][b.length];
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// ✅ Preload utilities
|
|
73
|
-
const preloadImage = (src: string): Promise<HTMLImageElement> => {
|
|
74
|
-
return new Promise((resolve) => {
|
|
75
|
-
// ✅ Проверяем, не загружена ли уже картинка
|
|
76
|
-
const img = new Image();
|
|
77
|
-
img.onload = () => resolve(img);
|
|
78
|
-
img.onerror = () => resolve(img); // Continue even on error, но img все равно создан
|
|
79
|
-
img.src = src;
|
|
80
|
-
});
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const preloadImages = async (urls: string[], signal?: AbortSignal): Promise<Map<string, HTMLImageElement>> => {
|
|
84
|
-
const imageMap = new Map<string, HTMLImageElement>();
|
|
85
|
-
const promises = urls.map(async (url) => {
|
|
86
|
-
const img = await preloadImage(url);
|
|
87
|
-
imageMap.set(url, img);
|
|
88
|
-
});
|
|
89
|
-
await Promise.allSettled(promises);
|
|
90
|
-
return imageMap;
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export default function Game({
|
|
94
|
-
gameCubeSize,
|
|
95
|
-
screenHeight,
|
|
96
|
-
screenWidth,
|
|
97
|
-
}: {
|
|
98
|
-
gameCubeSize?: number;
|
|
99
|
-
screenHeight?: number;
|
|
100
|
-
screenWidth?: number;
|
|
101
|
-
}) {
|
|
102
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
103
|
-
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
globalReset();
|
|
106
|
-
return () => {
|
|
107
|
-
// ✅ Восстанавливаем overflow на html и body
|
|
108
|
-
document.documentElement.style.overflow = "";
|
|
109
|
-
document.body.style.overflow = "";
|
|
110
|
-
|
|
111
|
-
// ✅ Удаляем наш style элемент
|
|
112
|
-
const style = document.getElementById("whats-missing-reset");
|
|
113
|
-
if (style) {
|
|
114
|
-
style.remove();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ✅ Cancel any ongoing preloads
|
|
118
|
-
if (abortController.current) {
|
|
119
|
-
abortController.current.abort();
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}, []);
|
|
123
|
-
|
|
124
|
-
const [theme, setTheme] = useState<Theme | null>(null);
|
|
125
|
-
const [files, setFiles] = useState<ImageItem[]>([]);
|
|
126
|
-
const [rounds, setRounds] = useState(4);
|
|
127
|
-
const [started, setStarted] = useState(false);
|
|
128
|
-
const [currentRound, setCurrentRound] = useState(1);
|
|
129
|
-
const [hiddenIndex, setHiddenIndex] = useState<number | null>(null);
|
|
130
|
-
const [phase, setPhase] = useState<"ready" | "memorize" | "guess">("ready");
|
|
131
|
-
const [readyTime, setReadyTime] = useState(10);
|
|
132
|
-
const [memorizeTime, setMemorizeTime] = useState(10);
|
|
133
|
-
const [timeLeft, setTimeLeft] = useState(20);
|
|
134
|
-
const [score, setScore] = useState(0);
|
|
135
|
-
const [finished, setFinished] = useState(false);
|
|
136
|
-
const [inputValue, setInputValue] = useState("");
|
|
137
|
-
const [answered, setAnswered] = useState(false);
|
|
138
|
-
const [animating, setAnimating] = useState(false);
|
|
139
|
-
const [result, setResult] = useState<"correct" | "almost" | "wrong" | null>(null);
|
|
140
|
-
const [resultsTable, setResultsTable] = useState<RoundResult[]>([]);
|
|
141
|
-
const [usedHidden, setUsedHidden] = useState<string[]>([]);
|
|
142
|
-
const [isMobile, setIsMobile] = useState(false);
|
|
143
|
-
const [scale, setScale] = useState(1);
|
|
144
|
-
const [containerSize, setContainerSize] = useState<number | null>(null);
|
|
145
|
-
const [isDesktopLayout, setIsDesktopLayout] = useState(false);
|
|
146
|
-
const [isIPadMiniPortrait, setIsIPadMiniPortrait] = useState(false);
|
|
147
|
-
const [isIPadMiniLandscape, setIsIPadMiniLandscape] = useState(false);
|
|
148
|
-
const [isIPadAirPortrait, setIsIPadAirPortrait] = useState(false);
|
|
149
|
-
const [isIPadAirLandscape, setIsIPadAirLandscape] = useState(false);
|
|
150
|
-
const [isSurfaceDuoPortrait, setIsSurfaceDuoPortrait] = useState(false);
|
|
151
|
-
const [isSurfaceDuoLandscape, setIsSurfaceDuoLandscape] = useState(false);
|
|
152
|
-
const [isIPadProPortrait, setIsIPadProPortrait] = useState(false);
|
|
153
|
-
const [isIPadProLandscape, setIsIPadProLandscape] = useState(false);
|
|
154
|
-
const [isHorizontalLayout, setIsHorizontalLayout] = useState(false);
|
|
155
|
-
const [isWideScreen, setIsWideScreen] = useState(false);
|
|
156
|
-
|
|
157
|
-
// ✅ Система предзагрузки изображений
|
|
158
|
-
const preloadedUrls = useRef<Set<string>>(new Set());
|
|
159
|
-
const preloadedImages = useRef<Map<string, HTMLImageElement>>(new Map());
|
|
160
|
-
const abortController = useRef<AbortController | null>(null);
|
|
161
|
-
|
|
162
|
-
// ✅ адаптив под мобилки, планшеты и десктоп
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
const resize = () => {
|
|
165
|
-
const width = screenWidth ?? window.innerWidth;
|
|
166
|
-
const height = screenHeight ?? window.innerHeight;
|
|
167
|
-
const mobile = width < 768 || (width === 926 && height === 428) || (width === 932 && height === 430); // iPhone 14 Pro Max в landscape тоже считается мобильным
|
|
168
|
-
const isLandscape = (width > height && mobile) || (width === 926 && height === 428) || (width === 932 && height === 430); // iPhone 14 Pro Max в landscape
|
|
169
|
-
const isSmallHeight = height < 700; // Nest Hub, маленькие экраны
|
|
170
|
-
const isWideScreen = width / height > 1.8; // ✅ Широкие экраны
|
|
171
|
-
|
|
172
|
-
// Определяем iPhone до 14 Pro Max в landscape режиме
|
|
173
|
-
// iPhone 14 Pro Max: 926x428 (landscape)
|
|
174
|
-
// iPhone 13 Pro Max: 926x428 (landscape)
|
|
175
|
-
// iPhone 12 Pro Max: 926x428 (landscape)
|
|
176
|
-
// iPhone 11 Pro Max: 896x414 (landscape)
|
|
177
|
-
// iPhone XR: 896x414 (landscape)
|
|
178
|
-
// iPhone XS Max: 896x414 (landscape)
|
|
179
|
-
// iPhone X: 812x375 (landscape)
|
|
180
|
-
// iPhone 8 Plus: 736x414 (landscape)
|
|
181
|
-
// iPhone 8: 667x375 (landscape)
|
|
182
|
-
// iPhone SE: 667x375 (landscape)
|
|
183
|
-
|
|
184
|
-
// iPhone XR: 896x414 (landscape)
|
|
185
|
-
const isIPhoneXRLandscape = isLandscape && width === 896 && height === 414;
|
|
186
|
-
|
|
187
|
-
// iPhone 12 Pro: 844x390 (landscape)
|
|
188
|
-
const isIPhone12ProLandscape = isLandscape && width === 844 && height === 390;
|
|
189
|
-
|
|
190
|
-
// Разрешение 1366x766 - ноутбуки и небольшие десктопы
|
|
191
|
-
const is1366x766 = width === 1366 && height === 766;
|
|
192
|
-
|
|
193
|
-
// Разрешение 1366x768 - ноутбуки и небольшие десктопы
|
|
194
|
-
const is1366x768 = width === 1366 && height === 768;
|
|
195
|
-
|
|
196
|
-
// Разрешение 1280x720 - ноутбуки и небольшие десктопы
|
|
197
|
-
const is1280x720 = width === 1280 && height === 720;
|
|
198
|
-
|
|
199
|
-
// Разрешение 1440x900 - ноутбуки и небольшие десктопы
|
|
200
|
-
const is1440x900 = width === 1440 && height === 900;
|
|
201
|
-
|
|
202
|
-
// iPad Mini/Air размеры
|
|
203
|
-
// iPad Mini: 768x1024 (portrait), 1024x768 (landscape) - Chrome DevTools размеры
|
|
204
|
-
// iPad Air: 820x1180 (portrait), 1180x820 (landscape)
|
|
205
|
-
const isIPadMiniPortrait = width === 768 && height === 1024;
|
|
206
|
-
const isIPadMiniLandscape = width === 1024 && height === 768;
|
|
207
|
-
const isIPadAirPortrait = width === 820 && height === 1180;
|
|
208
|
-
const isIPadAirLandscape = width === 1180 && height === 820;
|
|
209
|
-
|
|
210
|
-
// Surface DUO размеры
|
|
211
|
-
// Surface DUO: 540x720 (portrait), 720x540 (landscape)
|
|
212
|
-
const isSurfaceDuoPortrait = width === 540 && height === 720;
|
|
213
|
-
const isSurfaceDuoLandscape = width === 720 && height === 540;
|
|
214
|
-
|
|
215
|
-
// iPad Pro размеры
|
|
216
|
-
// iPad Pro: 1024x1366 (portrait), 1366x1024 (landscape)
|
|
217
|
-
const isIPadProPortrait = width === 1024 && height === 1366;
|
|
218
|
-
const isIPadProLandscape = width === 1366 && height === 1024;
|
|
219
|
-
|
|
220
|
-
// Популярные разрешения для горизонтального расположения кнопки Check
|
|
221
|
-
const desktopLayout = width >= 1200 && height >= 600 && !mobile;
|
|
222
|
-
setIsDesktopLayout(desktopLayout);
|
|
223
|
-
|
|
224
|
-
// Установка состояний для iPad и Surface DUO
|
|
225
|
-
setIsIPadMiniPortrait(isIPadMiniPortrait);
|
|
226
|
-
setIsIPadMiniLandscape(isIPadMiniLandscape);
|
|
227
|
-
setIsIPadAirPortrait(isIPadAirPortrait);
|
|
228
|
-
setIsIPadAirLandscape(isIPadAirLandscape);
|
|
229
|
-
setIsSurfaceDuoPortrait(isSurfaceDuoPortrait);
|
|
230
|
-
setIsSurfaceDuoLandscape(isSurfaceDuoLandscape);
|
|
231
|
-
setIsIPadProPortrait(isIPadProPortrait);
|
|
232
|
-
setIsIPadProLandscape(isIPadProLandscape);
|
|
233
|
-
|
|
234
|
-
// ✅ Вычисляем горизонтальный layout ОДИН РАЗ
|
|
235
|
-
const isHorizontal =
|
|
236
|
-
(mobile && width > height) ||
|
|
237
|
-
mobile || // ✅ ВСЕ мобильные устройства (включая portrait)
|
|
238
|
-
height < 700 ||
|
|
239
|
-
isWideScreen || // ✅ Широкие экраны
|
|
240
|
-
(width === 1366 && height === 766) ||
|
|
241
|
-
(width === 1366 && height === 768) ||
|
|
242
|
-
(width === 1280 && height === 720) ||
|
|
243
|
-
(width === 1440 && height === 900) ||
|
|
244
|
-
isIPadMiniPortrait ||
|
|
245
|
-
isIPadMiniLandscape ||
|
|
246
|
-
isIPadAirPortrait ||
|
|
247
|
-
isIPadAirLandscape ||
|
|
248
|
-
isSurfaceDuoPortrait ||
|
|
249
|
-
isSurfaceDuoLandscape ||
|
|
250
|
-
isIPadProPortrait ||
|
|
251
|
-
isIPadProLandscape ||
|
|
252
|
-
isDesktopLayout;
|
|
253
|
-
setIsHorizontalLayout(isHorizontal);
|
|
254
|
-
|
|
255
|
-
const isOldIPhoneLandscape = isLandscape && height <= 428; // до iPhone 14 Pro Max включительно
|
|
256
|
-
|
|
257
|
-
// treat only phones as "mobile" so tablets use the desktop (square/centered) layout
|
|
258
|
-
setIsMobile(mobile);
|
|
259
|
-
|
|
260
|
-
// ✅ Используем gameCubeSize от родительского сайта, но с защитой от слишком маленьких размеров
|
|
261
|
-
if (mobile) {
|
|
262
|
-
// Мобилки: используем переданный gameCubeSize или полную ширину
|
|
263
|
-
setContainerSize(gameCubeSize && gameCubeSize >= 320 ? gameCubeSize : null);
|
|
264
|
-
setScale(1);
|
|
265
|
-
} else if (isSmallHeight) {
|
|
266
|
-
// Маленькие экраны: используем переданный gameCubeSize или полную высоту
|
|
267
|
-
setContainerSize(gameCubeSize && gameCubeSize >= 400 ? gameCubeSize : null);
|
|
268
|
-
setScale(1);
|
|
269
|
-
} else if (isWideScreen) {
|
|
270
|
-
// ✅ Широкие экраны: используем стандартный размер но уменьшаем масштаб картинок
|
|
271
|
-
const minSize = 400;
|
|
272
|
-
const maxSize = 1200;
|
|
273
|
-
const finalSize = gameCubeSize
|
|
274
|
-
? Math.max(minSize, Math.min(maxSize, gameCubeSize))
|
|
275
|
-
: Math.min(1000, Math.min(width, height) * 0.9);
|
|
276
|
-
setContainerSize(finalSize);
|
|
277
|
-
setScale(0.85); // Уменьшаем масштаб картинок на 15%
|
|
278
|
-
} else {
|
|
279
|
-
// Десктопы: используем переданный gameCubeSize с разумными ограничениями
|
|
280
|
-
const minSize = 400;
|
|
281
|
-
const maxSize = 1200;
|
|
282
|
-
const finalSize = gameCubeSize
|
|
283
|
-
? Math.max(minSize, Math.min(maxSize, gameCubeSize))
|
|
284
|
-
: Math.min(1000, Math.min(width, height) * 0.9);
|
|
285
|
-
setContainerSize(finalSize);
|
|
286
|
-
setScale(1);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
resize();
|
|
290
|
-
window.addEventListener("resize", resize);
|
|
291
|
-
return () => window.removeEventListener("resize", resize);
|
|
292
|
-
}, [screenWidth, screenHeight, gameCubeSize]);
|
|
293
|
-
|
|
294
|
-
const getRandomSix = (arr: ImageItem[]) =>
|
|
295
|
-
[...arr].sort(() => Math.random() - 0.5).slice(0, 6);
|
|
296
|
-
|
|
297
|
-
// ✅ Функция предзагрузки темы
|
|
298
|
-
const startThemePreload = (themeName: Theme) => {
|
|
299
|
-
// Cancel previous preload
|
|
300
|
-
if (abortController.current) {
|
|
301
|
-
abortController.current.abort();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Create new controller
|
|
305
|
-
abortController.current = new AbortController();
|
|
306
|
-
|
|
307
|
-
// Get theme images and start preloading
|
|
308
|
-
const themeImages = themes[themeName];
|
|
309
|
-
const urls = themeImages.map(item => item.src);
|
|
310
|
-
|
|
311
|
-
preloadImages(urls, abortController.current.signal).then((imageMap) => {
|
|
312
|
-
// Mark as preloaded
|
|
313
|
-
urls.forEach(url => {
|
|
314
|
-
preloadedUrls.current.add(url);
|
|
315
|
-
if (imageMap.has(url)) {
|
|
316
|
-
preloadedImages.current.set(url, imageMap.get(url)!);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
}).catch(() => {
|
|
320
|
-
// Ignore abort errors
|
|
321
|
-
});
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
const startGame = () => {
|
|
325
|
-
if (!theme) return;
|
|
326
|
-
const selected = getRandomSix(themes[theme]);
|
|
327
|
-
setFiles(selected);
|
|
328
|
-
setStarted(true);
|
|
329
|
-
setFinished(false);
|
|
330
|
-
setCurrentRound(1);
|
|
331
|
-
setScore(0);
|
|
332
|
-
setResultsTable([]);
|
|
333
|
-
setUsedHidden([]);
|
|
334
|
-
|
|
335
|
-
// ✅ Preload current round images
|
|
336
|
-
const urls = selected.map(item => item.src);
|
|
337
|
-
preloadImages(urls, abortController.current?.signal).then((imageMap) => {
|
|
338
|
-
urls.forEach(url => {
|
|
339
|
-
preloadedUrls.current.add(url);
|
|
340
|
-
if (imageMap.has(url)) {
|
|
341
|
-
preloadedImages.current.set(url, imageMap.get(url)!);
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
}).catch(() => {
|
|
345
|
-
// Ignore abort errors
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
startRound(selected, [], true);
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
const startRound = (images = files, used: string[] = usedHidden, isFirst = false) => {
|
|
352
|
-
const roundSet = getRandomSix(images.length ? images : themes[theme!]);
|
|
353
|
-
let idx = Math.floor(Math.random() * roundSet.length);
|
|
354
|
-
let candidate = roundSet[idx].name;
|
|
355
|
-
let attempts = 0;
|
|
356
|
-
while (used.includes(candidate) && attempts < 20) {
|
|
357
|
-
idx = Math.floor(Math.random() * roundSet.length);
|
|
358
|
-
candidate = roundSet[idx].name;
|
|
359
|
-
attempts++;
|
|
360
|
-
}
|
|
361
|
-
setFiles(roundSet);
|
|
362
|
-
setHiddenIndex(idx);
|
|
363
|
-
setUsedHidden([...used, candidate]);
|
|
364
|
-
setAnswered(false);
|
|
365
|
-
setInputValue("");
|
|
366
|
-
setTimeLeft(20);
|
|
367
|
-
setResult(null);
|
|
368
|
-
|
|
369
|
-
// ✅ Preload next round images in background
|
|
370
|
-
if (theme && !isFirst) {
|
|
371
|
-
const nextPool = getRandomSix(themes[theme].filter(item => !used.includes(item.name)));
|
|
372
|
-
const nextUrls = nextPool.map(item => item.src);
|
|
373
|
-
preloadImages(nextUrls, abortController.current?.signal).then((imageMap) => {
|
|
374
|
-
nextUrls.forEach(url => {
|
|
375
|
-
preloadedUrls.current.add(url);
|
|
376
|
-
if (imageMap.has(url)) {
|
|
377
|
-
preloadedImages.current.set(url, imageMap.get(url)!);
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
}).catch(() => {
|
|
381
|
-
// Ignore abort errors
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (isFirst) {
|
|
386
|
-
setPhase("ready");
|
|
387
|
-
setReadyTime(10); // Увеличено до 10 секунд для VPN/медленных соединений
|
|
388
|
-
setMemorizeTime(10);
|
|
389
|
-
} else {
|
|
390
|
-
setPhase("memorize");
|
|
391
|
-
setMemorizeTime(10);
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
useEffect(() => {
|
|
396
|
-
if (!started || finished || answered) return;
|
|
397
|
-
if (phase === "ready") {
|
|
398
|
-
if (readyTime <= 0) setPhase("memorize");
|
|
399
|
-
else {
|
|
400
|
-
const t = setTimeout(() => setReadyTime((r) => r - 1), 1000);
|
|
401
|
-
return () => clearTimeout(t);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (phase === "memorize") {
|
|
405
|
-
if (memorizeTime <= 0) setPhase("guess");
|
|
406
|
-
else {
|
|
407
|
-
const t = setTimeout(() => setMemorizeTime((m) => m - 1), 1000);
|
|
408
|
-
return () => clearTimeout(t);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (phase === "guess") {
|
|
412
|
-
if (timeLeft <= 0) {
|
|
413
|
-
setAnswered(true);
|
|
414
|
-
setResult("wrong");
|
|
415
|
-
const correct = files[hiddenIndex!].name;
|
|
416
|
-
setResultsTable((prev) => [
|
|
417
|
-
...prev,
|
|
418
|
-
{ round: currentRound, answer: inputValue, correct, result: "wrong" },
|
|
419
|
-
]);
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const t = setTimeout(() => setTimeLeft((s) => s - 1), 1000);
|
|
423
|
-
return () => clearTimeout(t);
|
|
424
|
-
}
|
|
425
|
-
}, [phase, readyTime, memorizeTime, timeLeft, started, finished, answered, files, hiddenIndex, currentRound, inputValue]);
|
|
426
|
-
|
|
427
|
-
const checkAnswer = () => {
|
|
428
|
-
if (hiddenIndex === null || animating) return;
|
|
429
|
-
const correct = files[hiddenIndex].name;
|
|
430
|
-
const userAnswer = inputValue.toLowerCase().trim();
|
|
431
|
-
let roundResult: "correct" | "almost" | "wrong" = "wrong";
|
|
432
|
-
if (userAnswer === correct) {
|
|
433
|
-
setScore((s) => s + 1);
|
|
434
|
-
setResult("correct");
|
|
435
|
-
roundResult = "correct";
|
|
436
|
-
} else if (levenshtein(userAnswer, correct) === 1) {
|
|
437
|
-
setScore((s) => s + 0.5);
|
|
438
|
-
setResult("almost");
|
|
439
|
-
roundResult = "almost";
|
|
440
|
-
} else {
|
|
441
|
-
setResult("wrong");
|
|
442
|
-
roundResult = "wrong";
|
|
443
|
-
}
|
|
444
|
-
setResultsTable((prev) => [...prev, { round: currentRound, answer: userAnswer, correct, result: roundResult }]);
|
|
445
|
-
setAnimating(true);
|
|
446
|
-
setTimeout(() => {
|
|
447
|
-
setAnimating(false);
|
|
448
|
-
setAnswered(true);
|
|
449
|
-
}, 600);
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const nextRound = () =>
|
|
453
|
-
currentRound < rounds ? (setCurrentRound((r) => r + 1), startRound()) : setFinished(true);
|
|
454
|
-
|
|
455
|
-
const exitGame = () => {
|
|
456
|
-
// ✅ Cancel any ongoing preloads
|
|
457
|
-
if (abortController.current) {
|
|
458
|
-
abortController.current.abort();
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
setStarted(false);
|
|
462
|
-
setFinished(false);
|
|
463
|
-
setTheme(null);
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
const MemoizedLogo = useMemo(
|
|
467
|
-
() => {
|
|
468
|
-
// Скрываем логотип на мобильных устройствах в landscape режиме и на малых экранах
|
|
469
|
-
if ((isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700) {
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return (
|
|
474
|
-
// ensure logo is positioned inside the square container
|
|
475
|
-
<div style={{ ...styles.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }}>
|
|
476
|
-
<picture>
|
|
477
|
-
<source
|
|
478
|
-
srcSet={window.origin + "/cloud/speakid/games/whatsmissing/logo.svg"}
|
|
479
|
-
type="image/svg+xml"
|
|
480
|
-
/>
|
|
481
|
-
<img
|
|
482
|
-
src={window.origin + "/cloud/speakid/games/whatsmissing/logo.png"}
|
|
483
|
-
alt="SPEAKID Logo"
|
|
484
|
-
style={styles.gmLogoImg}
|
|
485
|
-
loading="lazy"
|
|
486
|
-
/>
|
|
487
|
-
</picture>
|
|
488
|
-
</div>
|
|
489
|
-
);
|
|
490
|
-
},
|
|
491
|
-
[isMobile]
|
|
492
|
-
);
|
|
493
|
-
|
|
494
|
-
return (
|
|
495
|
-
<div
|
|
496
|
-
ref={containerRef}
|
|
497
|
-
style={{
|
|
498
|
-
width: "100%",
|
|
499
|
-
height: "100%",
|
|
500
|
-
display: "flex",
|
|
501
|
-
justifyContent: "center",
|
|
502
|
-
alignItems: "center",
|
|
503
|
-
background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
|
|
504
|
-
transition: "background 0.3s ease",
|
|
505
|
-
overflow: "hidden",
|
|
506
|
-
position: "absolute",
|
|
507
|
-
top: 0,
|
|
508
|
-
left: 0,
|
|
509
|
-
right: 0,
|
|
510
|
-
bottom: 0
|
|
511
|
-
}}
|
|
512
|
-
>
|
|
513
|
-
<div
|
|
514
|
-
style={{
|
|
515
|
-
width: isMobile ? "100%" : (containerSize || gameCubeSize || 1000),
|
|
516
|
-
height: isMobile ? "100%" : (containerSize || gameCubeSize || 1000),
|
|
517
|
-
display: "flex",
|
|
518
|
-
justifyContent: "center",
|
|
519
|
-
alignItems: "center",
|
|
520
|
-
overflow: "hidden",
|
|
521
|
-
borderRadius: isMobile ? 0 : "20px",
|
|
522
|
-
background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
|
|
523
|
-
boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.1)",
|
|
524
|
-
margin: isMobile ? "0 auto" : "unset",
|
|
525
|
-
position: "relative", // needed so absolute logo is inside the square
|
|
526
|
-
transform: `scale(${scale})`, // ✅ Применяем масштаб для широких экранов
|
|
527
|
-
}}
|
|
528
|
-
>
|
|
529
|
-
<div
|
|
530
|
-
style={{
|
|
531
|
-
transform: "none",
|
|
532
|
-
width: "100%",
|
|
533
|
-
height: "100%",
|
|
534
|
-
display: "flex",
|
|
535
|
-
justifyContent: "center",
|
|
536
|
-
alignItems: "center",
|
|
537
|
-
}}
|
|
538
|
-
>
|
|
539
|
-
<div id="whats-missing-root">
|
|
540
|
-
{!isMobile && MemoizedLogo}
|
|
541
|
-
{/* ====== ИГРОВАЯ ЛОГИКА ====== */}
|
|
542
|
-
{!theme && !started && (
|
|
543
|
-
<div style={styles.gmCenterScreen}>
|
|
544
|
-
<h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
|
|
545
|
-
<p style={styles.gmBodyM}>Select a theme:</p>
|
|
546
|
-
<div style={{ display: "flex", gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "8px" : "16px" }}>
|
|
547
|
-
{["animals", "food", "toys"].map((t) => (
|
|
548
|
-
<button key={t} style={{
|
|
549
|
-
...styles.gmButton,
|
|
550
|
-
padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "8px 12px" : "12px 24px",
|
|
551
|
-
fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "12px" : "16px",
|
|
552
|
-
minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "70px" : "auto"
|
|
553
|
-
}} onClick={() => {
|
|
554
|
-
setTheme(t as Theme);
|
|
555
|
-
startThemePreload(t as Theme);
|
|
556
|
-
}}>
|
|
557
|
-
{t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
|
|
558
|
-
</button>
|
|
559
|
-
))}
|
|
560
|
-
</div>
|
|
561
|
-
<div style={{ marginTop: 24 }}>
|
|
562
|
-
<p style={styles.gmBodyS}>Choose number of rounds:</p>
|
|
563
|
-
<div style={{ display: "flex", gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "6px" : "12px", marginTop: 8 }}>
|
|
564
|
-
{[3, 4, 5].map((n) => (
|
|
565
|
-
<button
|
|
566
|
-
key={n}
|
|
567
|
-
style={{
|
|
568
|
-
...styles.gmButton,
|
|
569
|
-
...(rounds === n ? styles.gmButtonActive : {}),
|
|
570
|
-
padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "6px 10px" : "12px 24px",
|
|
571
|
-
fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "12px" : "16px",
|
|
572
|
-
minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "40px" : "auto"
|
|
573
|
-
}}
|
|
574
|
-
onClick={() => setRounds(n)}
|
|
575
|
-
>
|
|
576
|
-
{n}
|
|
577
|
-
</button>
|
|
578
|
-
))}
|
|
579
|
-
</div>
|
|
580
|
-
</div>
|
|
581
|
-
</div>
|
|
582
|
-
)}
|
|
583
|
-
{theme && !started && (
|
|
584
|
-
<div style={styles.gmCenterScreen}>
|
|
585
|
-
<h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
|
|
586
|
-
<p style={styles.gmBodyM}>Rounds: {rounds}</p>
|
|
587
|
-
<button style={{
|
|
588
|
-
...styles.gmButton,
|
|
589
|
-
padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "8px 16px" : "12px 24px",
|
|
590
|
-
fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "14px" : "16px",
|
|
591
|
-
minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "120px" : "auto"
|
|
592
|
-
}} onClick={startGame}>
|
|
593
|
-
▶ Start game
|
|
594
|
-
</button>
|
|
595
|
-
</div>
|
|
596
|
-
)}
|
|
597
|
-
{finished && (
|
|
598
|
-
<div style={styles.gmCenterScreen}>
|
|
599
|
-
<h1 style={{
|
|
600
|
-
...styles.gmHeadline1,
|
|
601
|
-
marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "0px" : styles.gmHeadline1.marginTop,
|
|
602
|
-
marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "2px" : styles.gmHeadline1.marginBottom
|
|
603
|
-
}}>Results</h1>
|
|
604
|
-
<h2 style={{
|
|
605
|
-
...styles.gmHeadline3,
|
|
606
|
-
marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "0px" : styles.gmHeadline3.marginTop,
|
|
607
|
-
marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "2px" : styles.gmHeadline3.marginBottom
|
|
608
|
-
}}>
|
|
609
|
-
Your score: {score} / {rounds}
|
|
610
|
-
</h2>
|
|
611
|
-
<p style={{
|
|
612
|
-
...styles.gmBodyM,
|
|
613
|
-
color: "#10b981",
|
|
614
|
-
marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "0px" : "12px",
|
|
615
|
-
marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "2px" : styles.gmBodyM.marginBottom
|
|
616
|
-
}}>Yahoo! You did it! 🍬✨</p>
|
|
617
|
-
<table style={{
|
|
618
|
-
...styles.gmTable,
|
|
619
|
-
marginTop: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "0px" : "20px",
|
|
620
|
-
marginBottom: (isMobile && window.innerWidth > window.innerHeight) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) || isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape ? "4px" : "32px"
|
|
621
|
-
}}>
|
|
622
|
-
<thead>
|
|
623
|
-
<tr>
|
|
624
|
-
<th>Round</th>
|
|
625
|
-
<th>Your Answer</th>
|
|
626
|
-
<th>Correct</th>
|
|
627
|
-
<th>Result</th>
|
|
628
|
-
</tr>
|
|
629
|
-
</thead>
|
|
630
|
-
<tbody>
|
|
631
|
-
{resultsTable.map((r, i) => (
|
|
632
|
-
<tr key={i}>
|
|
633
|
-
<td style={styles.gmTableCell}>{r.round}</td>
|
|
634
|
-
<td style={styles.gmTableCell}>{r.answer || "—"}</td>
|
|
635
|
-
<td style={styles.gmTableCell}>{r.correct}</td>
|
|
636
|
-
<td style={styles.gmTableCell}>
|
|
637
|
-
{r.result === "correct"
|
|
638
|
-
? "✔ Correct"
|
|
639
|
-
: r.result === "almost"
|
|
640
|
-
? "◐ Almost (0.5)"
|
|
641
|
-
: "✘ Wrong"}
|
|
642
|
-
</td>
|
|
643
|
-
</tr>
|
|
644
|
-
))}
|
|
645
|
-
</tbody>
|
|
646
|
-
</table>
|
|
647
|
-
<div style={{
|
|
648
|
-
display: "flex",
|
|
649
|
-
gap: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "6px" : "12px",
|
|
650
|
-
marginTop: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "2px" : (window.innerWidth === 1366 && window.innerHeight === 766) || (window.innerWidth === 1366 && window.innerHeight === 768) || (window.innerWidth === 1280 && window.innerHeight === 720) || (window.innerWidth === 1440 && window.innerHeight === 900) || isDesktopLayout ? "12px" : "24px"
|
|
651
|
-
}}>
|
|
652
|
-
<button style={{
|
|
653
|
-
...styles.gmButton,
|
|
654
|
-
padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "6px 10px" : "12px 24px",
|
|
655
|
-
fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "12px" : "16px",
|
|
656
|
-
minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "80px" : "auto"
|
|
657
|
-
}} onClick={startGame}>
|
|
658
|
-
🔁 Play again
|
|
659
|
-
</button>
|
|
660
|
-
<button style={{
|
|
661
|
-
...styles.gmButton,
|
|
662
|
-
padding: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "6px 10px" : "12px 24px",
|
|
663
|
-
fontSize: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "12px" : "16px",
|
|
664
|
-
minWidth: (isMobile && window.innerWidth > window.innerHeight) || (isMobile && window.innerWidth <= 375 && window.innerHeight <= 667) || (window.innerWidth === 896 && window.innerHeight === 414) || (window.innerWidth === 844 && window.innerHeight === 390) || (window.innerWidth === 926 && window.innerHeight === 428) || (window.innerWidth === 932 && window.innerHeight === 430) ? "80px" : "auto"
|
|
665
|
-
}} onClick={exitGame}>
|
|
666
|
-
⬅️ Choose theme
|
|
667
|
-
</button>
|
|
668
|
-
</div>
|
|
669
|
-
</div>
|
|
670
|
-
)}
|
|
671
|
-
{started && !finished && (
|
|
672
|
-
<div style={styles.gmGameLayout}>
|
|
673
|
-
<div
|
|
674
|
-
style={{
|
|
675
|
-
minHeight: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
676
|
-
? "45px" // iPhone SE: еще более компактная высота
|
|
677
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
678
|
-
? "45px" // iPhone до 14 Pro Max в landscape: такая же компактная как SE
|
|
679
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
680
|
-
? "45px" // iPhone XR в landscape: такая же компактная как SE
|
|
681
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
682
|
-
? "45px" // iPhone 12 Pro в landscape: такая же компактная как SE
|
|
683
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
684
|
-
? "45px" // iPhone 14 Pro Max в landscape: такая же компактная как SE
|
|
685
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
|
|
686
|
-
? "60px" // iPhone landscape и малые экраны: компактная высота
|
|
687
|
-
: "160px",
|
|
688
|
-
display: "flex",
|
|
689
|
-
flexDirection: "column",
|
|
690
|
-
justifyContent: "center",
|
|
691
|
-
alignItems: "center",
|
|
692
|
-
paddingTop: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
693
|
-
? "8px" // iPhone SE: поднимаем таймер еще выше
|
|
694
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
695
|
-
? "8px" // iPhone до 14 Pro Max в landscape: поднимаем еще выше
|
|
696
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
697
|
-
? "8px" // iPhone XR в landscape: поднимаем еще выше
|
|
698
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
699
|
-
? "8px" // iPhone 12 Pro в landscape: поднимаем еще выше
|
|
700
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
701
|
-
? "8px" // iPhone 14 Pro Max в landscape: поднимаем еще выше
|
|
702
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
|
|
703
|
-
? "15px" // iPhone landscape и малые экраны: поднимаем выше
|
|
704
|
-
: "40px"
|
|
705
|
-
}}
|
|
706
|
-
>
|
|
707
|
-
{phase === "ready" && (
|
|
708
|
-
<>
|
|
709
|
-
<h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
|
|
710
|
-
<div style={styles.gmHourglass}>⏳</div>
|
|
711
|
-
</>
|
|
712
|
-
)}
|
|
713
|
-
{phase === "memorize" && (
|
|
714
|
-
<p style={{ ...styles.gmBodyM, color: "#10b981" }}>
|
|
715
|
-
MEMORIZE ({memorizeTime})
|
|
716
|
-
</p>
|
|
717
|
-
)}
|
|
718
|
-
{phase === "guess" && !answered && (
|
|
719
|
-
<p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
|
|
720
|
-
)}
|
|
721
|
-
</div>
|
|
722
|
-
{phase !== "ready" && (
|
|
723
|
-
<div
|
|
724
|
-
style={{
|
|
725
|
-
...styles.gmGrid,
|
|
726
|
-
gridTemplateColumns: isMobile && window.innerWidth > window.innerHeight
|
|
727
|
-
? "repeat(3, 1fr)" // mobile landscape: 3 колонки
|
|
728
|
-
: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
729
|
-
? "repeat(3, 1fr)" // iPhone SE: 3 колонки для лучшего использования пространства
|
|
730
|
-
: isMobile
|
|
731
|
-
? "repeat(2, 1fr)" // mobile portrait: 2 колонки
|
|
732
|
-
: window.innerHeight < 700
|
|
733
|
-
? "repeat(3, 1fr)" // Nest Hub и малые экраны: 3 колонки
|
|
734
|
-
: "repeat(3, 210px)", // desktop: 3 колонки фиксированного размера
|
|
735
|
-
gridAutoRows: isMobile && window.innerWidth > window.innerHeight
|
|
736
|
-
? "120px" // mobile landscape: меньшая высота
|
|
737
|
-
: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
738
|
-
? "100px" // iPhone SE: очень компактная высота
|
|
739
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
740
|
-
? "100px" // iPhone XR в landscape: такая же компактная как SE
|
|
741
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
742
|
-
? "100px" // iPhone 12 Pro в landscape: такая же компактная как SE
|
|
743
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
744
|
-
? "100px" // iPhone 14 Pro Max в landscape: такая же компактная как SE
|
|
745
|
-
: isMobile
|
|
746
|
-
? "150px" // mobile portrait: стандартная высота
|
|
747
|
-
: window.innerHeight < 700
|
|
748
|
-
? "140px" // Nest Hub: компактная высота
|
|
749
|
-
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
750
|
-
? "240px" // Больше высота для экранов с масштабированием
|
|
751
|
-
: "210px", // desktop: стандартная высота
|
|
752
|
-
gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
753
|
-
? "12px" // iPhone SE: больше отступов
|
|
754
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
755
|
-
? "12px" // iPhone XR в landscape: такие же отступы как SE
|
|
756
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
757
|
-
? "12px" // iPhone 12 Pro в landscape: такие же отступы как SE
|
|
758
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
759
|
-
? "12px" // iPhone 14 Pro Max в landscape: такие же отступы как SE
|
|
760
|
-
: isMobile || window.innerHeight < 700
|
|
761
|
-
? "8px"
|
|
762
|
-
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
763
|
-
? "24px" // Больше отступы для экранов с масштабированием
|
|
764
|
-
: "20px",
|
|
765
|
-
justifyItems: "center",
|
|
766
|
-
maxWidth: isMobile && window.innerWidth > window.innerHeight
|
|
767
|
-
? "90%" // mobile landscape: используем больше ширины
|
|
768
|
-
: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
769
|
-
? "95%" // iPhone SE: используем почти всю ширину
|
|
770
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
771
|
-
? "95%" // iPhone XR в landscape: используем почти всю ширину как SE
|
|
772
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
773
|
-
? "95%" // iPhone 12 Pro в landscape: используем почти всю ширину как SE
|
|
774
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
775
|
-
? "95%" // iPhone 14 Pro Max в landscape: используем почти всю ширину как SE
|
|
776
|
-
: "100%",
|
|
777
|
-
padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
778
|
-
? "8px" // iPhone SE: минимальные внутренние отступы
|
|
779
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
780
|
-
? "8px" // iPhone XR в landscape: минимальные отступы как SE
|
|
781
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
782
|
-
? "8px" // iPhone 12 Pro в landscape: минимальные отступы как SE
|
|
783
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
784
|
-
? "8px" // iPhone 14 Pro Max в landscape: минимальные отступы как SE
|
|
785
|
-
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
786
|
-
? "20px" // Дополнительные отступы для экранов с масштабированием
|
|
787
|
-
: "16px",
|
|
788
|
-
}}
|
|
789
|
-
>
|
|
790
|
-
{files.map((file, i) => {
|
|
791
|
-
const isHidden =
|
|
792
|
-
hiddenIndex === i && phase === "guess" && !answered;
|
|
793
|
-
return (
|
|
794
|
-
<div
|
|
795
|
-
key={i}
|
|
796
|
-
style={{
|
|
797
|
-
width: "100%",
|
|
798
|
-
height: "100%",
|
|
799
|
-
borderRadius: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
800
|
-
? "6px" // iPhone SE: меньшие скругления
|
|
801
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
802
|
-
? "6px" // iPhone XR в landscape: меньшие скругления как SE
|
|
803
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
804
|
-
? "6px" // iPhone 12 Pro в landscape: меньшие скругления как SE
|
|
805
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
806
|
-
? "6px" // iPhone 14 Pro Max в landscape: меньшие скругления как SE
|
|
807
|
-
: "12px",
|
|
808
|
-
overflow: "hidden",
|
|
809
|
-
boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
810
|
-
? "0 1px 4px rgba(0,0,0,0.1)" // iPhone SE: меньшие тени
|
|
811
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
812
|
-
? "0 1px 4px rgba(0,0,0,0.1)" // iPhone XR в landscape: меньшие тени как SE
|
|
813
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
814
|
-
? "0 1px 4px rgba(0,0,0,0.1)" // iPhone 12 Pro в landscape: меньшие тени как SE
|
|
815
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
816
|
-
? "0 1px 4px rgba(0,0,0,0.1)" // iPhone 14 Pro Max в landscape: меньшие тени как SE
|
|
817
|
-
: "0 4px 12px rgba(0,0,0,0.15)",
|
|
818
|
-
transition: "all 0.3s ease",
|
|
819
|
-
cursor: "pointer",
|
|
820
|
-
...(result === "correct" && hiddenIndex === i
|
|
821
|
-
? {
|
|
822
|
-
boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
823
|
-
? "0 0 15px #10b981"
|
|
824
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
825
|
-
? "0 0 15px #10b981" // iPhone XR в landscape: такие же тени как SE
|
|
826
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
827
|
-
? "0 0 15px #10b981" // iPhone 12 Pro в landscape: такие же тени как SE
|
|
828
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
829
|
-
? "0 0 15px #10b981" // iPhone 14 Pro Max в landscape: такие же тени как SE
|
|
830
|
-
: "0 0 20px #10b981",
|
|
831
|
-
transform: "scale(1.03)"
|
|
832
|
-
}
|
|
833
|
-
: {}),
|
|
834
|
-
...(result === "wrong" && hiddenIndex === i
|
|
835
|
-
? {
|
|
836
|
-
boxShadow: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
837
|
-
? "0 0 15px #ef4444"
|
|
838
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
839
|
-
? "0 0 15px #ef4444" // iPhone XR в landscape: такие же тени как SE
|
|
840
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
841
|
-
? "0 0 15px #ef4444" // iPhone 12 Pro в landscape: такие же тени как SE
|
|
842
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
843
|
-
? "0 0 15px #ef4444" // iPhone 14 Pro Max в landscape: такие же тени как SE
|
|
844
|
-
: "0 0 20px #ef4444",
|
|
845
|
-
transform: "scale(1.03)"
|
|
846
|
-
}
|
|
847
|
-
: {}),
|
|
848
|
-
}}
|
|
849
|
-
>
|
|
850
|
-
{!isHidden && (() => {
|
|
851
|
-
// ✅ Используем уже загруженный объект Image чтобы браузер взял из кеша
|
|
852
|
-
const preloadedImg = preloadedImages.current.get(file.src);
|
|
853
|
-
// Используем src из уже загруженного объекта или оригинальный URL
|
|
854
|
-
const imgSrc = preloadedImg?.complete && preloadedImg.src
|
|
855
|
-
? preloadedImg.src
|
|
856
|
-
: file.src;
|
|
857
|
-
|
|
858
|
-
return (
|
|
859
|
-
<img
|
|
860
|
-
key={`${file.src}-${preloadedImg?.complete ? 'loaded' : 'pending'}`}
|
|
861
|
-
src={imgSrc}
|
|
862
|
-
alt={file.name}
|
|
863
|
-
fetchPriority="high"
|
|
864
|
-
loading="eager"
|
|
865
|
-
decoding="async"
|
|
866
|
-
style={{
|
|
867
|
-
width: "100%",
|
|
868
|
-
height: "100%",
|
|
869
|
-
objectFit: "cover",
|
|
870
|
-
}}
|
|
871
|
-
/>
|
|
872
|
-
);
|
|
873
|
-
})()}
|
|
874
|
-
</div>
|
|
875
|
-
);
|
|
876
|
-
})}
|
|
877
|
-
</div>
|
|
878
|
-
)}
|
|
879
|
-
<div style={{
|
|
880
|
-
marginTop: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
881
|
-
? "1px" // iPhone SE: еще более минимальный отступ
|
|
882
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
883
|
-
? "1px" // iPhone до 14 Pro Max в landscape: такой же как SE
|
|
884
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
885
|
-
? "1px" // iPhone XR в landscape: такой же как SE
|
|
886
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
887
|
-
? "1px" // iPhone 12 Pro в landscape: такой же как SE
|
|
888
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
889
|
-
? "1px" // iPhone 14 Pro Max в landscape: такой же как SE
|
|
890
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
|
|
891
|
-
? "4px" // iPhone landscape и малые экраны: минимальный отступ
|
|
892
|
-
: "16px",
|
|
893
|
-
height: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
894
|
-
? "40px" // iPhone SE: еще более компактная высота
|
|
895
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
896
|
-
? "40px" // iPhone до 14 Pro Max в landscape: такая же как SE
|
|
897
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
898
|
-
? "40px" // iPhone XR в landscape: такая же как SE
|
|
899
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
900
|
-
? "40px" // iPhone 12 Pro в landscape: такая же как SE
|
|
901
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
902
|
-
? "40px" // iPhone 14 Pro Max в landscape: такая же как SE
|
|
903
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700
|
|
904
|
-
? "50px" // iPhone landscape и малые экраны: компактная высота
|
|
905
|
-
: "80px"
|
|
906
|
-
}}>
|
|
907
|
-
{phase === "guess" && !answered && (
|
|
908
|
-
<div style={{
|
|
909
|
-
display: "flex",
|
|
910
|
-
flexDirection: isHorizontalLayout ? "row" : "column",
|
|
911
|
-
gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
912
|
-
? "3px" // iPhone SE: еще более минимальный отступ
|
|
913
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
914
|
-
? "3px" // iPhone до 14 Pro Max в landscape: такой же как SE
|
|
915
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
916
|
-
? "3px" // iPhone XR в landscape: такой же как SE
|
|
917
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
918
|
-
? "3px" // iPhone 12 Pro в landscape: такой же как SE
|
|
919
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
920
|
-
? "3px" // iPhone 14 Pro Max в landscape: такой же как SE
|
|
921
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
922
|
-
? "8px" // 1366x766: отступ между инпутом и кнопкой
|
|
923
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
924
|
-
? "8px" // 1366x768: отступ между инпутом и кнопкой
|
|
925
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
926
|
-
? "8px" // 1280x720: отступ между инпутом и кнопкой
|
|
927
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
928
|
-
? "8px" // 1440x900: отступ между инпутом и кнопкой
|
|
929
|
-
: isDesktopLayout // Десктопные разрешения
|
|
930
|
-
? "8px" // Десктопные разрешения: отступ между инпутом и кнопкой
|
|
931
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
932
|
-
? "8px" // iPad и Surface DUO: отступ между инпутом и кнопкой
|
|
933
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "6px" : "12px",
|
|
934
|
-
alignItems: "center",
|
|
935
|
-
justifyContent: "center",
|
|
936
|
-
width: "100%",
|
|
937
|
-
maxWidth: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
938
|
-
? "270px" // iPhone SE: еще более компактная ширина
|
|
939
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
940
|
-
? "270px" // iPhone до 14 Pro Max в landscape: такая же как SE
|
|
941
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
942
|
-
? "270px" // iPhone XR в landscape: такая же как SE
|
|
943
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
944
|
-
? "270px" // iPhone 12 Pro в landscape: такая же как SE
|
|
945
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
946
|
-
? "270px" // iPhone 14 Pro Max в landscape: такая же как SE
|
|
947
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
948
|
-
? "400px" // 1366x766: ширина контейнера
|
|
949
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
950
|
-
? "400px" // 1366x768: ширина контейнера
|
|
951
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
952
|
-
? "400px" // 1280x720: ширина контейнера
|
|
953
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
954
|
-
? "400px" // 1440x900: ширина контейнера
|
|
955
|
-
: isDesktopLayout // Десктопные разрешения
|
|
956
|
-
? "400px" // Десктопные разрешения: ширина контейнера
|
|
957
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
958
|
-
? "400px" // iPad и Surface DUO: ширина контейнера
|
|
959
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "400px" : "300px"
|
|
960
|
-
}}>
|
|
961
|
-
<>
|
|
962
|
-
<input
|
|
963
|
-
type="text"
|
|
964
|
-
placeholder="Type the missing word"
|
|
965
|
-
value={inputValue}
|
|
966
|
-
onChange={(e) => setInputValue(e.target.value)}
|
|
967
|
-
style={{
|
|
968
|
-
...styles.gmInput,
|
|
969
|
-
width: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
970
|
-
? "170px" // iPhone SE: еще более компактная ширина
|
|
971
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
972
|
-
? "170px" // iPhone до 14 Pro Max в landscape: такая же как SE
|
|
973
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
974
|
-
? "170px" // iPhone XR в landscape: такая же как SE
|
|
975
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
976
|
-
? "170px" // iPhone 12 Pro в landscape: такая же как SE
|
|
977
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
978
|
-
? "170px" // iPhone 14 Pro Max в landscape: такая же как SE
|
|
979
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
980
|
-
? "250px" // 1366x766: ширина инпута
|
|
981
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
982
|
-
? "250px" // 1366x768: ширина инпута
|
|
983
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
984
|
-
? "250px" // 1280x720: ширина инпута
|
|
985
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
986
|
-
? "250px" // 1440x900: ширина инпута
|
|
987
|
-
: isDesktopLayout // Десктопные разрешения
|
|
988
|
-
? "250px" // Десктопные разрешения: ширина инпута
|
|
989
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
990
|
-
? "250px" // iPad и Surface DUO: ширина инпута
|
|
991
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "250px" : "auto",
|
|
992
|
-
padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
993
|
-
? "5px 6px" // iPhone SE: еще более компактный padding
|
|
994
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
995
|
-
? "5px 6px" // iPhone до 14 Pro Max в landscape: такой же как SE
|
|
996
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
997
|
-
? "5px 6px" // iPhone XR в landscape: такой же как SE
|
|
998
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
999
|
-
? "5px 6px" // iPhone 12 Pro в landscape: такой же как SE
|
|
1000
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1001
|
-
? "5px 6px" // iPhone 14 Pro Max в landscape: такой же как SE
|
|
1002
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1003
|
-
? "10px 12px" // 1366x766: padding инпута
|
|
1004
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1005
|
-
? "10px 12px" // 1366x768: padding инпута
|
|
1006
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1007
|
-
? "10px 12px" // 1280x720: padding инпута
|
|
1008
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1009
|
-
? "10px 12px" // 1440x900: padding инпута
|
|
1010
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1011
|
-
? "10px 12px" // Десктопные разрешения: padding инпута
|
|
1012
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1013
|
-
? "10px 12px" // iPad и Surface DUO: padding инпута
|
|
1014
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 12px" : "12px 16px",
|
|
1015
|
-
fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1016
|
-
? "10px" // iPhone SE: еще меньший шрифт
|
|
1017
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1018
|
-
? "10px" // iPhone до 14 Pro Max в landscape: такой же как SE
|
|
1019
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1020
|
-
? "11px" // iPhone XR в landscape: компактный шрифт
|
|
1021
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1022
|
-
? "10px" // iPhone 12 Pro в landscape: такой же как SE
|
|
1023
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1024
|
-
? "10px" // iPhone 14 Pro Max в landscape: такой же как SE
|
|
1025
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1026
|
-
? "14px" // 1366x766: размер шрифта инпута
|
|
1027
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1028
|
-
? "14px" // 1366x768: размер шрифта инпута
|
|
1029
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1030
|
-
? "14px" // 1280x720: размер шрифта инпута
|
|
1031
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1032
|
-
? "14px" // 1440x900: размер шрифта инпута
|
|
1033
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1034
|
-
? "14px" // Десктопные разрешения: размер шрифта инпута
|
|
1035
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1036
|
-
? "14px" // iPad и Surface DUO: размер шрифта инпута
|
|
1037
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px",
|
|
1038
|
-
flex: isHorizontalLayout ? "1" : "none"
|
|
1039
|
-
}}
|
|
1040
|
-
/>
|
|
1041
|
-
<button
|
|
1042
|
-
style={{
|
|
1043
|
-
...styles.gmButton,
|
|
1044
|
-
marginLeft: isHorizontalLayout ? "8px" : "0",
|
|
1045
|
-
padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1046
|
-
? "5px 8px" // iPhone SE: еще более компактный padding
|
|
1047
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1048
|
-
? "6px 10px" // iPhone до 14 Pro Max в landscape: компактный padding
|
|
1049
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1050
|
-
? "6px 10px" // iPhone XR в landscape: компактный padding
|
|
1051
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1052
|
-
? "6px 10px" // iPhone 12 Pro в landscape: компактный padding
|
|
1053
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1054
|
-
? "6px 10px" // iPhone 14 Pro Max в landscape: компактный padding
|
|
1055
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1056
|
-
? "10px 16px" // 1366x766: padding кнопки Check
|
|
1057
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1058
|
-
? "10px 16px" // 1366x768: padding кнопки Check
|
|
1059
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1060
|
-
? "10px 16px" // 1280x720: padding кнопки Check
|
|
1061
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1062
|
-
? "10px 16px" // 1440x900: padding кнопки Check
|
|
1063
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1064
|
-
? "10px 16px" // Десктопные разрешения: padding кнопки Check
|
|
1065
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1066
|
-
? "10px 16px" // iPad и Surface DUO: padding кнопки Check
|
|
1067
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 16px" : "12px 24px",
|
|
1068
|
-
fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1069
|
-
? "10px" // iPhone SE: еще меньший шрифт
|
|
1070
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1071
|
-
? "11px" // iPhone до 14 Pro Max в landscape: компактный шрифт
|
|
1072
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1073
|
-
? "11px" // iPhone XR в landscape: компактный шрифт
|
|
1074
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1075
|
-
? "11px" // iPhone 12 Pro в landscape: компактный шрифт
|
|
1076
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1077
|
-
? "11px" // iPhone 14 Pro Max в landscape: компактный шрифт
|
|
1078
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1079
|
-
? "14px" // 1366x766: размер шрифта кнопки Check
|
|
1080
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1081
|
-
? "14px" // 1366x768: размер шрифта кнопки Check
|
|
1082
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1083
|
-
? "14px" // 1280x720: размер шрифта кнопки Check
|
|
1084
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1085
|
-
? "14px" // 1440x900: размер шрифта кнопки Check
|
|
1086
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1087
|
-
? "14px" // Десктопные разрешения: размер шрифта кнопки Check
|
|
1088
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1089
|
-
? "14px" // iPad и Surface DUO: размер шрифта кнопки Check
|
|
1090
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px",
|
|
1091
|
-
minWidth: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1092
|
-
? "45px" // iPhone SE: еще более компактная ширина
|
|
1093
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1094
|
-
? "55px" // iPhone до 14 Pro Max в landscape: компактная ширина
|
|
1095
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1096
|
-
? "55px" // iPhone XR в landscape: компактная ширина
|
|
1097
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1098
|
-
? "55px" // iPhone 12 Pro в landscape: компактная ширина
|
|
1099
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1100
|
-
? "55px" // iPhone 14 Pro Max в landscape: компактная ширина
|
|
1101
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1102
|
-
? "80px" // 1366x766: минимальная ширина кнопки Check
|
|
1103
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1104
|
-
? "80px" // 1366x768: минимальная ширина кнопки Check
|
|
1105
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1106
|
-
? "80px" // 1280x720: минимальная ширина кнопки Check
|
|
1107
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1108
|
-
? "80px" // 1440x900: минимальная ширина кнопки Check
|
|
1109
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1110
|
-
? "80px" // Десктопные разрешения: минимальная ширина кнопки Check
|
|
1111
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1112
|
-
? "80px" // iPad и Surface DUO: минимальная ширина кнопки Check
|
|
1113
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "80px" : "100px",
|
|
1114
|
-
flexShrink: 0
|
|
1115
|
-
}}
|
|
1116
|
-
onClick={checkAnswer}
|
|
1117
|
-
disabled={animating}
|
|
1118
|
-
>
|
|
1119
|
-
{animating ? "..." : "Check"}
|
|
1120
|
-
</button>
|
|
1121
|
-
</>
|
|
1122
|
-
</div>
|
|
1123
|
-
)}
|
|
1124
|
-
{answered && (
|
|
1125
|
-
<button style={{
|
|
1126
|
-
...styles.gmButton,
|
|
1127
|
-
padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1128
|
-
? "5px 8px" // iPhone SE: еще более компактный padding
|
|
1129
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1130
|
-
? "6px 10px" // iPhone до 14 Pro Max в landscape: компактный padding
|
|
1131
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1132
|
-
? "6px 10px" // iPhone XR в landscape: компактный padding
|
|
1133
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1134
|
-
? "6px 10px" // iPhone 12 Pro в landscape: компактный padding
|
|
1135
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1136
|
-
? "6px 10px" // iPhone 14 Pro Max в landscape: компактный padding
|
|
1137
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1138
|
-
? "10px 16px" // 1366x766: padding кнопки Next round
|
|
1139
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1140
|
-
? "10px 16px" // 1366x768: padding кнопки Next round
|
|
1141
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1142
|
-
? "10px 16px" // 1280x720: padding кнопки Next round
|
|
1143
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1144
|
-
? "10px 16px" // 1440x900: padding кнопки Next round
|
|
1145
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1146
|
-
? "10px 16px" // Десктопные разрешения: padding кнопки Next round
|
|
1147
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1148
|
-
? "10px 16px" // iPad и Surface DUO: padding кнопки Next round
|
|
1149
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "10px 16px" : "12px 24px",
|
|
1150
|
-
fontSize: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
1151
|
-
? "10px" // iPhone SE: еще меньший шрифт
|
|
1152
|
-
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
1153
|
-
? "11px" // iPhone до 14 Pro Max в landscape: компактный шрифт
|
|
1154
|
-
: (window.innerWidth === 896 && window.innerHeight === 414) // iPhone XR
|
|
1155
|
-
? "11px" // iPhone XR в landscape: компактный шрифт
|
|
1156
|
-
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
1157
|
-
? "11px" // iPhone 12 Pro в landscape: компактный шрифт
|
|
1158
|
-
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
1159
|
-
? "11px" // iPhone 14 Pro Max в landscape: компактный шрифт
|
|
1160
|
-
: (window.innerWidth === 1366 && window.innerHeight === 766) // 1366x766
|
|
1161
|
-
? "14px" // 1366x766: размер шрифта кнопки Next round
|
|
1162
|
-
: (window.innerWidth === 1366 && window.innerHeight === 768) // 1366x768
|
|
1163
|
-
? "14px" // 1366x768: размер шрифта кнопки Next round
|
|
1164
|
-
: (window.innerWidth === 1280 && window.innerHeight === 720) // 1280x720
|
|
1165
|
-
? "14px" // 1280x720: размер шрифта кнопки Next round
|
|
1166
|
-
: (window.innerWidth === 1440 && window.innerHeight === 900) // 1440x900
|
|
1167
|
-
? "14px" // 1440x900: размер шрифта кнопки Next round
|
|
1168
|
-
: isDesktopLayout // Десктопные разрешения
|
|
1169
|
-
? "14px" // Десктопные разрешения: размер шрифта кнопки Next round
|
|
1170
|
-
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
1171
|
-
? "14px" // iPad и Surface DUO: размер шрифта кнопки Next round
|
|
1172
|
-
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px"
|
|
1173
|
-
}} onClick={nextRound}>
|
|
1174
|
-
Next round
|
|
1175
|
-
</button>
|
|
1176
|
-
)}
|
|
1177
|
-
</div>
|
|
1178
|
-
</div>
|
|
1179
|
-
)}
|
|
1180
|
-
</div>
|
|
1181
|
-
</div>
|
|
1182
|
-
</div>
|
|
1183
|
-
</div>
|
|
1184
|
-
);
|
|
1185
|
-
}
|