@mblinkov/whats-missing 20.0.45 → 20.0.46
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/CHANGELOG.md +219 -0
- package/dist/whats-missing.cjs.js +3 -3
- package/dist/whats-missing.es.js +217 -180
- package/package.json +1 -1
- package/src/Game.tsx +115 -15
package/package.json
CHANGED
package/src/Game.tsx
CHANGED
|
@@ -14,6 +14,8 @@ type RoundResult = {
|
|
|
14
14
|
// ✅ базовый reset
|
|
15
15
|
const globalReset = () => {
|
|
16
16
|
const style = document.createElement("style");
|
|
17
|
+
style.id = "whats-missing-reset"; // ✅ Добавляем ID для удаления
|
|
18
|
+
|
|
17
19
|
style.textContent = `
|
|
18
20
|
#whats-missing-root, #whats-missing-root * {
|
|
19
21
|
box-sizing: border-box;
|
|
@@ -41,6 +43,13 @@ const globalReset = () => {
|
|
|
41
43
|
overflow: hidden !important;
|
|
42
44
|
}
|
|
43
45
|
`;
|
|
46
|
+
|
|
47
|
+
// ✅ Удаляем старый стиль, если он есть
|
|
48
|
+
const existingStyle = document.getElementById("whats-missing-reset");
|
|
49
|
+
if (existingStyle) {
|
|
50
|
+
existingStyle.remove();
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
document.head.appendChild(style);
|
|
45
54
|
};
|
|
46
55
|
|
|
@@ -74,7 +83,15 @@ export default function Game({
|
|
|
74
83
|
useEffect(() => {
|
|
75
84
|
globalReset();
|
|
76
85
|
return () => {
|
|
86
|
+
// ✅ Восстанавливаем overflow на html и body
|
|
87
|
+
document.documentElement.style.overflow = "";
|
|
77
88
|
document.body.style.overflow = "";
|
|
89
|
+
|
|
90
|
+
// ✅ Удаляем наш style элемент
|
|
91
|
+
const style = document.getElementById("whats-missing-reset");
|
|
92
|
+
if (style) {
|
|
93
|
+
style.remove();
|
|
94
|
+
}
|
|
78
95
|
};
|
|
79
96
|
}, []);
|
|
80
97
|
|
|
@@ -108,6 +125,9 @@ export default function Game({
|
|
|
108
125
|
const [isSurfaceDuoLandscape, setIsSurfaceDuoLandscape] = useState(false);
|
|
109
126
|
const [isIPadProPortrait, setIsIPadProPortrait] = useState(false);
|
|
110
127
|
const [isIPadProLandscape, setIsIPadProLandscape] = useState(false);
|
|
128
|
+
const [isHorizontalLayout, setIsHorizontalLayout] = useState(false);
|
|
129
|
+
const [loadedImages, setLoadedImages] = useState<Set<string>>(new Set());
|
|
130
|
+
const [preloadQueue, setPreloadQueue] = useState<string[]>([]);
|
|
111
131
|
|
|
112
132
|
// ✅ адаптив под мобилки, планшеты и десктоп
|
|
113
133
|
useEffect(() => {
|
|
@@ -180,6 +200,25 @@ export default function Game({
|
|
|
180
200
|
setIsIPadProPortrait(isIPadProPortrait);
|
|
181
201
|
setIsIPadProLandscape(isIPadProLandscape);
|
|
182
202
|
|
|
203
|
+
// ✅ Вычисляем горизонтальный layout ОДИН РАЗ
|
|
204
|
+
const isHorizontal =
|
|
205
|
+
(mobile && width > height) ||
|
|
206
|
+
height < 700 ||
|
|
207
|
+
(width === 1366 && height === 766) ||
|
|
208
|
+
(width === 1366 && height === 768) ||
|
|
209
|
+
(width === 1280 && height === 720) ||
|
|
210
|
+
(width === 1440 && height === 900) ||
|
|
211
|
+
isIPadMiniPortrait ||
|
|
212
|
+
isIPadMiniLandscape ||
|
|
213
|
+
isIPadAirPortrait ||
|
|
214
|
+
isIPadAirLandscape ||
|
|
215
|
+
isSurfaceDuoPortrait ||
|
|
216
|
+
isSurfaceDuoLandscape ||
|
|
217
|
+
isIPadProPortrait ||
|
|
218
|
+
isIPadProLandscape ||
|
|
219
|
+
isDesktopLayout;
|
|
220
|
+
setIsHorizontalLayout(isHorizontal);
|
|
221
|
+
|
|
183
222
|
const isOldIPhoneLandscape = isLandscape && height <= 428; // до iPhone 14 Pro Max включительно
|
|
184
223
|
|
|
185
224
|
// treat only phones as "mobile" so tablets use the desktop (square/centered) layout
|
|
@@ -213,9 +252,33 @@ export default function Game({
|
|
|
213
252
|
const getRandomSix = (arr: ImageItem[]) =>
|
|
214
253
|
[...arr].sort(() => Math.random() - 0.5).slice(0, 6);
|
|
215
254
|
|
|
216
|
-
|
|
255
|
+
// ✅ Функции предзагрузки изображений
|
|
256
|
+
const preloadImage = (src: string): Promise<void> => {
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
const img = new Image();
|
|
259
|
+
img.onload = () => {
|
|
260
|
+
setLoadedImages(prev => new Set(prev).add(src));
|
|
261
|
+
resolve();
|
|
262
|
+
};
|
|
263
|
+
img.onerror = reject;
|
|
264
|
+
img.src = src;
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const preloadImages = async (urls: string[]) => {
|
|
269
|
+
// Загружаем с задержкой, чтобы не блокировать UI
|
|
270
|
+
const promises = urls.map(url => preloadImage(url));
|
|
271
|
+
await Promise.allSettled(promises);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const startGame = async () => {
|
|
217
275
|
if (!theme) return;
|
|
276
|
+
|
|
218
277
|
const selected = getRandomSix(themes[theme]);
|
|
278
|
+
|
|
279
|
+
// ✅ Предзагружаем текущие изображения с высоким приоритетом
|
|
280
|
+
await preloadImages(selected.map(item => item.src));
|
|
281
|
+
|
|
219
282
|
setFiles(selected);
|
|
220
283
|
setStarted(true);
|
|
221
284
|
setFinished(false);
|
|
@@ -223,6 +286,13 @@ export default function Game({
|
|
|
223
286
|
setScore(0);
|
|
224
287
|
setResultsTable([]);
|
|
225
288
|
setUsedHidden([]);
|
|
289
|
+
|
|
290
|
+
// ✅ Загружаем следующий раунд в фоне
|
|
291
|
+
const nextBatch = getRandomSix(
|
|
292
|
+
themes[theme].filter(item => !selected.includes(item))
|
|
293
|
+
);
|
|
294
|
+
preloadImages(nextBatch.map(item => item.src)); // Не ждем завершения
|
|
295
|
+
|
|
226
296
|
startRound(selected, [], true);
|
|
227
297
|
};
|
|
228
298
|
|
|
@@ -598,6 +668,8 @@ export default function Game({
|
|
|
598
668
|
? "150px" // mobile portrait: стандартная высота
|
|
599
669
|
: window.innerHeight < 700
|
|
600
670
|
? "140px" // Nest Hub: компактная высота
|
|
671
|
+
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
672
|
+
? "240px" // Больше высота для экранов с масштабированием
|
|
601
673
|
: "210px", // desktop: стандартная высота
|
|
602
674
|
gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
603
675
|
? "12px" // iPhone SE: больше отступов
|
|
@@ -607,7 +679,11 @@ export default function Game({
|
|
|
607
679
|
? "12px" // iPhone 12 Pro в landscape: такие же отступы как SE
|
|
608
680
|
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
609
681
|
? "12px" // iPhone 14 Pro Max в landscape: такие же отступы как SE
|
|
610
|
-
: isMobile || window.innerHeight < 700
|
|
682
|
+
: isMobile || window.innerHeight < 700
|
|
683
|
+
? "8px"
|
|
684
|
+
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
685
|
+
? "24px" // Больше отступы для экранов с масштабированием
|
|
686
|
+
: "20px",
|
|
611
687
|
justifyItems: "center",
|
|
612
688
|
maxWidth: isMobile && window.innerWidth > window.innerHeight
|
|
613
689
|
? "90%" // mobile landscape: используем больше ширины
|
|
@@ -626,8 +702,10 @@ export default function Game({
|
|
|
626
702
|
? "8px" // iPhone XR в landscape: минимальные отступы как SE
|
|
627
703
|
: (window.innerWidth === 844 && window.innerHeight === 390) // iPhone 12 Pro
|
|
628
704
|
? "8px" // iPhone 12 Pro в landscape: минимальные отступы как SE
|
|
629
|
-
|
|
705
|
+
: (window.innerWidth === 926 && window.innerHeight === 428) // iPhone 14 Pro Max
|
|
630
706
|
? "8px" // iPhone 14 Pro Max в landscape: минимальные отступы как SE
|
|
707
|
+
: (typeof window !== "undefined" && window.devicePixelRatio >= 1.25 && window.devicePixelRatio <= 1.5) // Среднее масштабирование (125%-150%)
|
|
708
|
+
? "20px" // Дополнительные отступы для экранов с масштабированием
|
|
631
709
|
: "16px",
|
|
632
710
|
}}
|
|
633
711
|
>
|
|
@@ -692,15 +770,37 @@ export default function Game({
|
|
|
692
770
|
}}
|
|
693
771
|
>
|
|
694
772
|
{!isHidden && (
|
|
695
|
-
<
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
773
|
+
<div style={{ position: "relative", width: "100%", height: "100%" }}>
|
|
774
|
+
{!loadedImages.has(file.src) && (
|
|
775
|
+
<div style={{
|
|
776
|
+
position: "absolute",
|
|
777
|
+
top: 0,
|
|
778
|
+
left: 0,
|
|
779
|
+
width: "100%",
|
|
780
|
+
height: "100%",
|
|
781
|
+
background: "linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%)",
|
|
782
|
+
backgroundSize: "200% 100%",
|
|
783
|
+
borderRadius: "inherit",
|
|
784
|
+
display: "flex",
|
|
785
|
+
justifyContent: "center",
|
|
786
|
+
alignItems: "center"
|
|
787
|
+
}} />
|
|
788
|
+
)}
|
|
789
|
+
<img
|
|
790
|
+
src={file.src}
|
|
791
|
+
alt={file.name}
|
|
792
|
+
// @ts-ignore - fetchpriority is a newer API
|
|
793
|
+
fetchpriority={loadedImages.has(file.src) ? "auto" : "high"}
|
|
794
|
+
onLoad={() => setLoadedImages(prev => new Set(prev).add(file.src))}
|
|
795
|
+
style={{
|
|
796
|
+
width: "100%",
|
|
797
|
+
height: "100%",
|
|
798
|
+
objectFit: "cover",
|
|
799
|
+
opacity: loadedImages.has(file.src) ? 1 : 0,
|
|
800
|
+
transition: "opacity 0.3s ease"
|
|
801
|
+
}}
|
|
802
|
+
/>
|
|
803
|
+
</div>
|
|
704
804
|
)}
|
|
705
805
|
</div>
|
|
706
806
|
);
|
|
@@ -738,7 +838,7 @@ export default function Game({
|
|
|
738
838
|
{phase === "guess" && !answered && (
|
|
739
839
|
<div style={{
|
|
740
840
|
display: "flex",
|
|
741
|
-
flexDirection:
|
|
841
|
+
flexDirection: isHorizontalLayout ? "row" : "column",
|
|
742
842
|
gap: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
743
843
|
? "3px" // iPhone SE: еще более минимальный отступ
|
|
744
844
|
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|
|
@@ -866,13 +966,13 @@ export default function Game({
|
|
|
866
966
|
: isIPadMiniPortrait || isIPadMiniLandscape || isIPadAirPortrait || isIPadAirLandscape || isSurfaceDuoPortrait || isSurfaceDuoLandscape || isIPadProPortrait || isIPadProLandscape
|
|
867
967
|
? "14px" // iPad и Surface DUO: размер шрифта инпута
|
|
868
968
|
: (isMobile && window.innerWidth > window.innerHeight) || window.innerHeight < 700 ? "14px" : "16px",
|
|
869
|
-
flex:
|
|
969
|
+
flex: isHorizontalLayout ? "1" : "none"
|
|
870
970
|
}}
|
|
871
971
|
/>
|
|
872
972
|
<button
|
|
873
973
|
style={{
|
|
874
974
|
...styles.gmButton,
|
|
875
|
-
marginLeft:
|
|
975
|
+
marginLeft: isHorizontalLayout ? "8px" : "0",
|
|
876
976
|
padding: isMobile && window.innerWidth <= 375 && window.innerHeight <= 667
|
|
877
977
|
? "5px 8px" // iPhone SE: еще более компактный padding
|
|
878
978
|
: (isMobile && window.innerWidth > window.innerHeight && window.innerHeight <= 428)
|