@mblinkov/whats-missing 20.0.27 → 20.0.29
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/whats-missing.cjs.js +3 -3
- package/dist/whats-missing.es.js +244 -254
- package/package.json +1 -1
- package/src/Game.styles.ts +5 -2
- package/src/Game.tsx +255 -241
package/package.json
CHANGED
package/src/Game.styles.ts
CHANGED
|
@@ -101,7 +101,8 @@ export const styles: Record<string, CSSProperties> = {
|
|
|
101
101
|
gmGameLayout: {
|
|
102
102
|
position: "relative",
|
|
103
103
|
width: "100%",
|
|
104
|
-
|
|
104
|
+
// allow the layout to expand within the square container for tablets/laptops
|
|
105
|
+
maxWidth: "none",
|
|
105
106
|
// should fill the parent square, not the full viewport
|
|
106
107
|
minHeight: "100%",
|
|
107
108
|
display: "flex",
|
|
@@ -116,9 +117,11 @@ export const styles: Record<string, CSSProperties> = {
|
|
|
116
117
|
|
|
117
118
|
gmGrid: {
|
|
118
119
|
width: "100%",
|
|
119
|
-
|
|
120
|
+
// let the grid use all available space; sizing is controlled in Game.tsx
|
|
121
|
+
maxWidth: "none",
|
|
120
122
|
margin: "0 auto",
|
|
121
123
|
display: "grid",
|
|
124
|
+
// defaults; overridden responsively in Game.tsx
|
|
122
125
|
gridTemplateColumns: "repeat(3, 210px)",
|
|
123
126
|
gridAutoRows: "210px",
|
|
124
127
|
gap: "20px",
|
package/src/Game.tsx
CHANGED
|
@@ -37,7 +37,7 @@ const levenshtein = (a: string, b: string) => {
|
|
|
37
37
|
return dp[a.length][b.length];
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
// ✅ фикс визуального подзума
|
|
40
|
+
// ✅ фикс визуального подзума
|
|
41
41
|
export function useNeutralizeBodyZoom(
|
|
42
42
|
ref: React.RefObject<HTMLDivElement | null>,
|
|
43
43
|
opts: { minDesktopWidth?: number; forceResize?: boolean } = { minDesktopWidth: 1200, forceResize: false }
|
|
@@ -58,7 +58,6 @@ export function useNeutralizeBodyZoom(
|
|
|
58
58
|
if (zoomInline && !isNaN(zoomInline) && zoomInline > 0) return zoomInline;
|
|
59
59
|
const zoomComputed = parseFloat((cs.zoom as string) || "");
|
|
60
60
|
if (zoomComputed && !isNaN(zoomComputed) && zoomComputed > 0) return zoomComputed;
|
|
61
|
-
|
|
62
61
|
const probe = document.createElement("div");
|
|
63
62
|
probe.style.position = "absolute";
|
|
64
63
|
probe.style.left = "0";
|
|
@@ -66,7 +65,6 @@ export function useNeutralizeBodyZoom(
|
|
|
66
65
|
probe.style.width = "100px";
|
|
67
66
|
probe.style.height = "1px";
|
|
68
67
|
probe.style.visibility = "hidden";
|
|
69
|
-
probe.style.pointerEvents = "none";
|
|
70
68
|
document.documentElement.appendChild(probe);
|
|
71
69
|
const rect = probe.getBoundingClientRect();
|
|
72
70
|
document.documentElement.removeChild(probe);
|
|
@@ -188,6 +186,22 @@ export default function Game({
|
|
|
188
186
|
const [usedHidden, setUsedHidden] = useState<string[]>([]);
|
|
189
187
|
const [isMobile, setIsMobile] = useState(false);
|
|
190
188
|
|
|
189
|
+
// simple responsive flag; we still compute exact sizes from the measured square
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
if (typeof window === "undefined") return;
|
|
192
|
+
const update = () => {
|
|
193
|
+
const w = screenWidth ?? window.innerWidth;
|
|
194
|
+
setIsMobile(w <= 768);
|
|
195
|
+
};
|
|
196
|
+
update();
|
|
197
|
+
window.addEventListener("resize", update);
|
|
198
|
+
window.addEventListener("orientationchange", update);
|
|
199
|
+
return () => {
|
|
200
|
+
window.removeEventListener("resize", update);
|
|
201
|
+
window.removeEventListener("orientationchange", update);
|
|
202
|
+
};
|
|
203
|
+
}, [screenWidth]);
|
|
204
|
+
|
|
191
205
|
useLayoutEffect(() => {
|
|
192
206
|
const el = squareRef.current;
|
|
193
207
|
if (!el || typeof window === "undefined") return;
|
|
@@ -270,7 +284,10 @@ export default function Game({
|
|
|
270
284
|
setAnswered(true);
|
|
271
285
|
setResult("wrong");
|
|
272
286
|
const correct = files[hiddenIndex!].name;
|
|
273
|
-
setResultsTable((prev) => [
|
|
287
|
+
setResultsTable((prev) => [
|
|
288
|
+
...prev,
|
|
289
|
+
{ round: currentRound, answer: inputValue, correct, result: "wrong" },
|
|
290
|
+
]);
|
|
274
291
|
return;
|
|
275
292
|
}
|
|
276
293
|
const t = setTimeout(() => setTimeLeft((s) => s - 1), 1000);
|
|
@@ -332,6 +349,17 @@ export default function Game({
|
|
|
332
349
|
[]
|
|
333
350
|
);
|
|
334
351
|
|
|
352
|
+
// ✅ масштаб интерфейса под реальное разрешение
|
|
353
|
+
const baseWidth = 1440;
|
|
354
|
+
const baseHeight = 900;
|
|
355
|
+
const scale =
|
|
356
|
+
typeof window !== "undefined"
|
|
357
|
+
? Math.min(
|
|
358
|
+
(screenWidth ?? window.innerWidth) / baseWidth,
|
|
359
|
+
(screenHeight ?? window.innerHeight) / baseHeight
|
|
360
|
+
)
|
|
361
|
+
: 1;
|
|
362
|
+
|
|
335
363
|
return (
|
|
336
364
|
<div
|
|
337
365
|
ref={containerRef}
|
|
@@ -342,10 +370,8 @@ export default function Game({
|
|
|
342
370
|
justifyContent: "center",
|
|
343
371
|
alignItems: "center",
|
|
344
372
|
background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
|
|
345
|
-
transition: "background 0.3s ease",
|
|
346
373
|
overflow: "hidden",
|
|
347
374
|
zIndex: 1,
|
|
348
|
-
pointerEvents: "auto",
|
|
349
375
|
}}
|
|
350
376
|
>
|
|
351
377
|
<div
|
|
@@ -357,267 +383,255 @@ export default function Game({
|
|
|
357
383
|
height: isMobile
|
|
358
384
|
? "100%"
|
|
359
385
|
: `${Math.min(gameCubeSize ?? 1000, screenWidth ?? 1000, screenHeight ?? 1000)}px`,
|
|
360
|
-
maxWidth: "calc(100vw - 24px)",
|
|
361
|
-
maxHeight: "calc(100vh - 20px)",
|
|
362
|
-
display: "flex",
|
|
363
|
-
justifyContent: "center",
|
|
364
|
-
alignItems: "center",
|
|
365
|
-
overflow: "hidden",
|
|
366
386
|
borderRadius: isMobile ? 0 : "20px",
|
|
367
387
|
background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
|
|
368
|
-
boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.08)",
|
|
369
388
|
position: "relative",
|
|
389
|
+
overflow: "hidden",
|
|
390
|
+
boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.08)",
|
|
391
|
+
display: "flex",
|
|
392
|
+
justifyContent: "center",
|
|
393
|
+
alignItems: "center",
|
|
370
394
|
}}
|
|
371
395
|
>
|
|
372
396
|
<div
|
|
397
|
+
id="whats-missing-root"
|
|
373
398
|
style={{
|
|
374
|
-
transform:
|
|
375
|
-
transformOrigin: "
|
|
376
|
-
width:
|
|
377
|
-
height:
|
|
399
|
+
transform: `scale(${scale})`,
|
|
400
|
+
transformOrigin: "center center",
|
|
401
|
+
width: `${100 / scale}%`,
|
|
402
|
+
height: `${100 / scale}%`,
|
|
378
403
|
display: "flex",
|
|
404
|
+
flexDirection: "column",
|
|
379
405
|
justifyContent: "center",
|
|
380
406
|
alignItems: "center",
|
|
381
407
|
}}
|
|
382
408
|
>
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
409
|
+
{!isMobile && MemoizedLogo}
|
|
410
|
+
|
|
411
|
+
{/* ====== ЛОББИ ====== */}
|
|
412
|
+
{!theme && !started && (
|
|
413
|
+
<div style={styles.gmCenterScreen}>
|
|
414
|
+
<h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
|
|
415
|
+
<p style={styles.gmBodyM}>Select a theme:</p>
|
|
416
|
+
<div style={{ display: "flex", gap: 16 }}>
|
|
417
|
+
{["animals", "food", "toys"].map((t) => (
|
|
418
|
+
<button key={t} style={styles.gmButton} onClick={() => setTheme(t as Theme)}>
|
|
419
|
+
{t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
|
|
420
|
+
</button>
|
|
421
|
+
))}
|
|
422
|
+
</div>
|
|
423
|
+
<div style={{ marginTop: 24 }}>
|
|
424
|
+
<p style={styles.gmBodyS}>Choose number of rounds:</p>
|
|
425
|
+
<div style={{ display: "flex", gap: 12, marginTop: 8 }}>
|
|
426
|
+
{[3, 4, 5].map((n) => (
|
|
392
427
|
<button
|
|
393
|
-
key={
|
|
394
|
-
style={
|
|
395
|
-
|
|
428
|
+
key={n}
|
|
429
|
+
style={{
|
|
430
|
+
...styles.gmButton,
|
|
431
|
+
...(rounds === n ? styles.gmButtonActive : {}),
|
|
432
|
+
}}
|
|
433
|
+
onClick={() => setRounds(n)}
|
|
396
434
|
>
|
|
397
|
-
{
|
|
398
|
-
? "🐶 Animals"
|
|
399
|
-
: t === "food"
|
|
400
|
-
? "🍎 Food"
|
|
401
|
-
: "🧸 Toys"}
|
|
435
|
+
{n}
|
|
402
436
|
</button>
|
|
403
437
|
))}
|
|
404
438
|
</div>
|
|
405
|
-
<div style={{ marginTop: 24 }}>
|
|
406
|
-
<p style={styles.gmBodyS}>Choose number of rounds:</p>
|
|
407
|
-
<div style={{ display: "flex", gap: 12, marginTop: 8 }}>
|
|
408
|
-
{[3, 4, 5].map((n) => (
|
|
409
|
-
<button
|
|
410
|
-
key={n}
|
|
411
|
-
style={{
|
|
412
|
-
...styles.gmButton,
|
|
413
|
-
...(rounds === n ? styles.gmButtonActive : {}),
|
|
414
|
-
}}
|
|
415
|
-
onClick={() => setRounds(n)}
|
|
416
|
-
>
|
|
417
|
-
{n}
|
|
418
|
-
</button>
|
|
419
|
-
))}
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
439
|
</div>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
440
|
+
</div>
|
|
441
|
+
)}
|
|
442
|
+
|
|
443
|
+
{/* ====== ПОДТВЕРЖДЕНИЕ ====== */}
|
|
444
|
+
{theme && !started && (
|
|
445
|
+
<div style={styles.gmCenterScreen}>
|
|
446
|
+
<h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
|
|
447
|
+
<p style={styles.gmBodyM}>Rounds: {rounds}</p>
|
|
448
|
+
<button style={styles.gmButton} onClick={startGame}>
|
|
449
|
+
▶ Start game
|
|
450
|
+
</button>
|
|
451
|
+
</div>
|
|
452
|
+
)}
|
|
453
|
+
|
|
454
|
+
{/* ====== РЕЗУЛЬТАТЫ ====== */}
|
|
455
|
+
{finished && (
|
|
456
|
+
<div style={styles.gmCenterScreen}>
|
|
457
|
+
<h1 style={styles.gmHeadline1}>Results</h1>
|
|
458
|
+
<h2 style={styles.gmHeadline3}>
|
|
459
|
+
Your score: {score} / {rounds}
|
|
460
|
+
</h2>
|
|
461
|
+
<p style={{ ...styles.gmBodyM, color: "#10b981", marginTop: 12 }}>
|
|
462
|
+
Yahoo! You did it! 🍬✨
|
|
463
|
+
</p>
|
|
464
|
+
<table style={styles.gmTable}>
|
|
465
|
+
<thead>
|
|
466
|
+
<tr>
|
|
467
|
+
<th>Round</th>
|
|
468
|
+
<th>Your Answer</th>
|
|
469
|
+
<th>Correct</th>
|
|
470
|
+
<th>Result</th>
|
|
471
|
+
</tr>
|
|
472
|
+
</thead>
|
|
473
|
+
<tbody>
|
|
474
|
+
{resultsTable.map((r, i) => (
|
|
475
|
+
<tr key={i}>
|
|
476
|
+
<td style={styles.gmTableCell}>{r.round}</td>
|
|
477
|
+
<td style={styles.gmTableCell}>{r.answer || "—"}</td>
|
|
478
|
+
<td style={styles.gmTableCell}>{r.correct}</td>
|
|
479
|
+
<td style={styles.gmTableCell}>
|
|
480
|
+
{r.result === "correct"
|
|
481
|
+
? "✔ Correct"
|
|
482
|
+
: r.result === "almost"
|
|
483
|
+
? "◐ Almost (0.5)"
|
|
484
|
+
: "✘ Wrong"}
|
|
485
|
+
</td>
|
|
486
|
+
</tr>
|
|
487
|
+
))}
|
|
488
|
+
</tbody>
|
|
489
|
+
</table>
|
|
490
|
+
<div style={{ display: "flex", gap: 12, marginTop: 24 }}>
|
|
429
491
|
<button style={styles.gmButton} onClick={startGame}>
|
|
430
|
-
|
|
492
|
+
🔁 Play again
|
|
493
|
+
</button>
|
|
494
|
+
<button style={styles.gmButton} onClick={exitGame}>
|
|
495
|
+
⬅️ Choose theme
|
|
431
496
|
</button>
|
|
432
497
|
</div>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
{r.result === "correct"
|
|
463
|
-
? "✔ Correct"
|
|
464
|
-
: r.result === "almost"
|
|
465
|
-
? "◐ Almost (0.5)"
|
|
466
|
-
: "✘ Wrong"}
|
|
467
|
-
</td>
|
|
468
|
-
</tr>
|
|
469
|
-
))}
|
|
470
|
-
</tbody>
|
|
471
|
-
</table>
|
|
472
|
-
<div style={{ display: "flex", gap: 12, marginTop: 24 }}>
|
|
473
|
-
<button style={styles.gmButton} onClick={startGame}>
|
|
474
|
-
🔁 Play again
|
|
475
|
-
</button>
|
|
476
|
-
<button style={styles.gmButton} onClick={exitGame}>
|
|
477
|
-
⬅️ Choose theme
|
|
478
|
-
</button>
|
|
479
|
-
</div>
|
|
498
|
+
</div>
|
|
499
|
+
)}
|
|
500
|
+
|
|
501
|
+
{/* ====== ИГРОВОЙ ЭКРАН ====== */}
|
|
502
|
+
{started && !finished && (
|
|
503
|
+
<div style={styles.gmGameLayout}>
|
|
504
|
+
<div
|
|
505
|
+
style={{
|
|
506
|
+
minHeight: 160,
|
|
507
|
+
display: "flex",
|
|
508
|
+
flexDirection: "column",
|
|
509
|
+
justifyContent: "center",
|
|
510
|
+
alignItems: "center",
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
{phase === "ready" && (
|
|
514
|
+
<>
|
|
515
|
+
<h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
|
|
516
|
+
<div style={styles.gmHourglass}>⏳</div>
|
|
517
|
+
</>
|
|
518
|
+
)}
|
|
519
|
+
{phase === "memorize" && (
|
|
520
|
+
<p style={{ ...styles.gmBodyM, color: "#10b981" }}>
|
|
521
|
+
MEMORIZE ({memorizeTime})
|
|
522
|
+
</p>
|
|
523
|
+
)}
|
|
524
|
+
{phase === "guess" && !answered && (
|
|
525
|
+
<p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
|
|
526
|
+
)}
|
|
480
527
|
</div>
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
<img
|
|
572
|
-
src={file.src}
|
|
573
|
-
alt={file.name}
|
|
574
|
-
style={{
|
|
575
|
-
width: "100%",
|
|
576
|
-
height: "100%",
|
|
577
|
-
objectFit: "cover",
|
|
578
|
-
display: "block",
|
|
579
|
-
}}
|
|
580
|
-
/>
|
|
581
|
-
)}
|
|
582
|
-
</div>
|
|
583
|
-
);
|
|
584
|
-
})}
|
|
585
|
-
</div>
|
|
586
|
-
);
|
|
587
|
-
})()}
|
|
588
|
-
{/* Инпут и кнопки */}
|
|
589
|
-
<div style={{ marginTop: 16, height: 80 }}>
|
|
590
|
-
{phase === "guess" && !answered && (
|
|
591
|
-
<div>
|
|
592
|
-
<input
|
|
593
|
-
type="text"
|
|
594
|
-
placeholder="Type the missing word"
|
|
595
|
-
value={inputValue}
|
|
596
|
-
onChange={(e) => setInputValue(e.target.value)}
|
|
597
|
-
style={styles.gmInput}
|
|
598
|
-
/>
|
|
599
|
-
<button
|
|
600
|
-
style={{ ...styles.gmButton, marginLeft: 8 }}
|
|
601
|
-
onClick={checkAnswer}
|
|
602
|
-
disabled={animating}
|
|
603
|
-
>
|
|
604
|
-
{animating ? "..." : "Check"}
|
|
605
|
-
</button>
|
|
606
|
-
</div>
|
|
607
|
-
)}
|
|
608
|
-
{answered && (
|
|
528
|
+
|
|
529
|
+
{/* Сетка карточек */}
|
|
530
|
+
{phase !== "ready" && (() => {
|
|
531
|
+
const gap = isMobile ? 12 : 20;
|
|
532
|
+
const innerGutters = 32; // total padding inside the grid wrapper
|
|
533
|
+
const effW = squareRef.current?.clientWidth ?? Math.min(gameCubeSize ?? 1000, screenWidth ?? 1000);
|
|
534
|
+
const effH = squareRef.current?.clientHeight ?? Math.min(gameCubeSize ?? 1000, screenHeight ?? 1000);
|
|
535
|
+
const effective = Math.min(effW, effH);
|
|
536
|
+
|
|
537
|
+
// choose a comfortable minimum card size per breakpoint
|
|
538
|
+
const vw = typeof window !== "undefined" ? window.innerWidth : (screenWidth ?? 1000);
|
|
539
|
+
const minCardBase = vw < 600 ? 110 : vw < 1024 ? 140 : 180;
|
|
540
|
+
|
|
541
|
+
// compute columns to best fill available space without going below minCardBase
|
|
542
|
+
const maxColumns = 4;
|
|
543
|
+
const minColumns = 2;
|
|
544
|
+
const possibleColumns = Math.max(
|
|
545
|
+
minColumns,
|
|
546
|
+
Math.min(
|
|
547
|
+
maxColumns,
|
|
548
|
+
Math.floor((effective - innerGutters + gap) / (minCardBase + gap)) || minColumns
|
|
549
|
+
)
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
const columns = possibleColumns;
|
|
553
|
+
const computed = Math.floor((effective - innerGutters - gap * (columns - 1)) / columns);
|
|
554
|
+
const cardSize = Math.max(minCardBase, computed);
|
|
555
|
+
|
|
556
|
+
return (
|
|
557
|
+
<div
|
|
558
|
+
style={{
|
|
559
|
+
...styles.gmGrid,
|
|
560
|
+
gridTemplateColumns: `repeat(${columns}, ${cardSize}px)`,
|
|
561
|
+
gridAutoRows: `${cardSize}px`,
|
|
562
|
+
gap: `${gap}px`,
|
|
563
|
+
justifyContent: "center",
|
|
564
|
+
justifyItems: "center",
|
|
565
|
+
padding: 8,
|
|
566
|
+
boxSizing: "border-box",
|
|
567
|
+
}}
|
|
568
|
+
>
|
|
569
|
+
{files.map((file, i) => {
|
|
570
|
+
const isHidden = hiddenIndex === i && phase === "guess" && !answered;
|
|
571
|
+
return (
|
|
572
|
+
<div
|
|
573
|
+
key={i}
|
|
574
|
+
style={{
|
|
575
|
+
...styles.gmCard,
|
|
576
|
+
width: `${cardSize}px`,
|
|
577
|
+
height: `${cardSize}px`,
|
|
578
|
+
padding: cardSize >= 160 ? 10 : 8,
|
|
579
|
+
boxSizing: "border-box",
|
|
580
|
+
...(result === "correct" && hiddenIndex === i
|
|
581
|
+
? styles.gmCorrect
|
|
582
|
+
: {}),
|
|
583
|
+
...(result === "wrong" && hiddenIndex === i
|
|
584
|
+
? styles.gmWrong
|
|
585
|
+
: {}),
|
|
586
|
+
}}
|
|
587
|
+
>
|
|
588
|
+
{!isHidden && (
|
|
589
|
+
<img
|
|
590
|
+
src={file.src}
|
|
591
|
+
alt={file.name}
|
|
592
|
+
style={{
|
|
593
|
+
width: "100%",
|
|
594
|
+
height: "100%",
|
|
595
|
+
objectFit: "cover",
|
|
596
|
+
display: "block",
|
|
597
|
+
}}
|
|
598
|
+
/>
|
|
599
|
+
)}
|
|
600
|
+
</div>
|
|
601
|
+
);
|
|
602
|
+
})}
|
|
603
|
+
</div>
|
|
604
|
+
);
|
|
605
|
+
})()}
|
|
606
|
+
|
|
607
|
+
{/* Инпут и кнопки */}
|
|
608
|
+
<div style={{ marginTop: 16, height: 80 }}>
|
|
609
|
+
{phase === "guess" && !answered && (
|
|
610
|
+
<div>
|
|
611
|
+
<input
|
|
612
|
+
type="text"
|
|
613
|
+
placeholder="Type the missing word"
|
|
614
|
+
value={inputValue}
|
|
615
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
616
|
+
style={styles.gmInput}
|
|
617
|
+
/>
|
|
609
618
|
<button
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
619
|
+
style={{ ...styles.gmButton, marginLeft: 8 }}
|
|
620
|
+
onClick={checkAnswer}
|
|
621
|
+
disabled={animating}
|
|
613
622
|
>
|
|
614
|
-
|
|
623
|
+
{animating ? "..." : "Check"}
|
|
615
624
|
</button>
|
|
616
|
-
|
|
617
|
-
|
|
625
|
+
</div>
|
|
626
|
+
)}
|
|
627
|
+
{answered && (
|
|
628
|
+
<button type="button" style={styles.gmButton} onClick={nextRound}>
|
|
629
|
+
Next round
|
|
630
|
+
</button>
|
|
631
|
+
)}
|
|
618
632
|
</div>
|
|
619
|
-
|
|
620
|
-
|
|
633
|
+
</div>
|
|
634
|
+
)}
|
|
621
635
|
</div>
|
|
622
636
|
</div>
|
|
623
637
|
</div>
|