@mblinkov/whats-missing 20.0.20 → 20.0.21
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 +109 -102
- package/package.json +1 -1
- package/src/Game.styles.ts +7 -3
- package/src/Game.tsx +48 -28
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),ce=window.origin+"/cloud/speakid/games/whatsmissing/",D=(a,m)=>a.map(([c,s])=>({src:`${ce}${m}/${s.replace(/\s/g,"%20")}.png`,name:s})),me=[["apple","apple"],["banana","banana"],["bread","bread"],["burger","burger"],["cake","cake"],["carrot","carrot"],["cheese","cheese"],["chicken","chicken"],["chocolate","chocolate"],["corn","corn"],["donut","donut"],["fish","fish"],["fries","fries"],["grapes","grapes"],["ice_cream","ice cream"],["milk","milk"],["orange","orange"],["pear","pear"],["pizza","pizza"],["rice","rice"],["sandwich","sandwich"],["soup","soup"],["strawberry","strawberry"],["tomato","tomato"],["watermelon","watermelon"]],ue=[["bear","bear"],["cat","cat"],["crocodile","crocodile"],["dolphin","dolphin"],["elephant","elephant"],["fox","fox"],["giraffe","giraffe"],["horse","horse"],["kangaroo","kangaroo"],["lion","lion"],["monkey","monkey"],["owl","owl"],["panda","panda"],["penguin","penguin"],["squirrel","squirrel"],["tiger","tiger"],["tortoise","tortoise"],["wolf","wolf"],["zebra","zebra"]],ge=[["ball","ball"],["balloon","balloon"],["blocks","blocks"],["car","car"],["doll","doll"],["drum","drum"],["guitar","guitar"],["jump_rope","jump rope"],["kite","kite"],["paints","paints"],["plane","plane"],["puzzle","puzzle"],["rocket","rocket"],["scooter","scooter"],["skateboard","skateboard"],["slide","slide"],["teddy_bear","teddy bear"],["yo-yo","yo-yo"],["crayons","crayons"]],P={food:D(me,"food"),animals:D(ue,"animals"),toys:D(ge,"toys")},he=`
|
|
2
2
|
@keyframes spin {
|
|
3
3
|
from { transform: rotate(0deg); }
|
|
4
4
|
to { transform: rotate(360deg); }
|
|
5
5
|
}
|
|
6
|
-
`;if(typeof document<"u"&&!document.getElementById("spin-keyframes")){const a=document.createElement("style");a.id="spin-keyframes",a.innerHTML=
|
|
6
|
+
`;if(typeof document<"u"&&!document.getElementById("spin-keyframes")){const a=document.createElement("style");a.id="spin-keyframes",a.innerHTML=he,document.head.appendChild(a)}const fe={animation:"spin 1.4s linear infinite"},n={gmCenterScreen:{position:"relative",zIndex:1,minHeight:"100%",width:"100%",display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",textAlign:"center",color:"#1f2937",padding:"24px 16px",boxSizing:"border-box",transform:"translateY(20px)"},gmHeadline1:{fontWeight:700,fontSize:"clamp(28px, 4vw, 40px)",lineHeight:"110%"},gmHeadline3:{fontWeight:600,fontSize:"18px",lineHeight:"130%"},gmBodyM:{fontWeight:400,fontSize:"16px",lineHeight:"140%"},gmBodyS:{fontWeight:400,fontSize:"14px",lineHeight:"140%",color:"#6b7280"},gmButton:{fontFamily:'"Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans"',fontWeight:600,fontSize:"16px",padding:"10px 16px",borderRadius:"12px",border:"1px solid #e5e7eb",background:"#ec4c44",color:"#ffffff",cursor:"pointer",boxShadow:"0 6px 18px rgba(236, 76, 68, .18)",transition:"transform .06s ease, box-shadow .2s ease, background .2s ease, opacity .2s ease"},gmButtonActive:{background:"#333",color:"#fff"},gmGameLayout:{position:"relative",width:"100%",maxWidth:"600px",minHeight:"100%",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",color:"#1f2937",padding:"16px 8px",margin:"0 auto"},gmGrid:{width:"100%",maxWidth:"calc(210px * 3 + 20px * 2)",margin:"0 auto",display:"grid",gridTemplateColumns:"repeat(3, 210px)",gridAutoRows:"210px",gap:"20px",justifyContent:"center"},gmCard:{border:"1px solid #e5e7eb",borderRadius:"16px",background:"#f9f9f9",aspectRatio:"1 / 1",display:"flex",alignItems:"center",justifyContent:"center",overflow:"hidden",transition:"transform .2s ease, box-shadow .2s ease, border-color .2s ease"},gmCorrect:{border:"2px solid #10b981",boxShadow:"0 0 18px rgba(16,185,129,.45)",background:"#ecfdf5"},gmWrong:{border:"2px solid #ec4c44",boxShadow:"0 0 18px rgba(236,76,68,.35)",background:"#fef2f2"},gmInput:{padding:"6px 10px",borderRadius:"6px",border:"1px solid #ccc",fontSize:"16px",fontFamily:'"Geist", system-ui',width:"160px"},gmTable:{marginTop:"20px",marginBottom:"32px",borderCollapse:"collapse",width:"100%",maxWidth:"520px",tableLayout:"fixed",textAlign:"center"},gmTableCell:{padding:"8px 12px",borderBottom:"1px solid #e5e7eb",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},gmLogoFixed:{position:"absolute",top:"16px",left:"24px",width:"120px",zIndex:10,pointerEvents:"none",background:"transparent",transform:"none",willChange:"auto"},gmLogoImg:{height:"clamp(28px, 5vw, 40px)",width:"auto",background:"transparent",objectFit:"contain",imageRendering:"auto",transform:"translateZ(0)",backfaceVisibility:"hidden",WebkitFontSmoothing:"antialiased"},gmHourglass:{fontSize:"42px",...fe}},pe=()=>{const a=document.createElement("style");a.textContent=`
|
|
7
7
|
/* scope only inside game root */
|
|
8
8
|
#whats-missing-root, #whats-missing-root * { box-sizing: border-box; }
|
|
9
9
|
#whats-missing-root img { max-width:100%; height:auto; display:block; user-select:none; }
|
|
10
|
-
`,document.head.appendChild(a)},
|
|
10
|
+
`,document.head.appendChild(a)},ye=(a,m)=>{const c=Array.from({length:a.length+1},()=>Array(m.length+1).fill(0));for(let s=0;s<=a.length;s++)c[s][0]=s;for(let s=0;s<=m.length;s++)c[0][s]=s;for(let s=1;s<=a.length;s++)for(let u=1;u<=m.length;u++)c[s][u]=a[s-1]===m[u-1]?c[s-1][u-1]:Math.min(c[s-1][u-1],c[s][u-1],c[s-1][u])+1;return c[a.length][m.length]};function xe(a,m={minDesktopWidth:1200,forceResize:!1}){const c=r.useRef(null),s=m.minDesktopWidth??1200,u=!!m.forceResize;r.useLayoutEffect(()=>{if(typeof window>"u"||!a||!a.current)return;const l=a.current,A=()=>{try{const i=document.body,d=getComputedStyle(i),p=parseFloat(i.style&&i.style.zoom||"");if(p&&!isNaN(p)&&p>0)return p;const v=parseFloat(d.zoom||"");if(v&&!isNaN(v)&&v>0)return v;const k=d.transform||d.webkitTransform||d.mozTransform;if(k&&k!=="none"){const z=k.match(/matrix\(([^)]+)\)/);if(z){const j=z[1].split(",").map(E=>parseFloat(E))[0];if(j&&!isNaN(j))return j}}}catch{}return 1},b=()=>{if(s&&window.innerWidth<s){S();return}const i=A();if(!i||i===1){S();return}c.current||(c.current={transform:l.style.transform||null,transformOrigin:l.style.transformOrigin||null,width:l.style.width||null,height:l.style.height||null,willChange:l.style.willChange||null});const d=1/i;u&&(l.style.width=`${i*100}%`,l.style.height=`${i*100}%`),l.style.transform=`scale(${d})`,l.style.transformOrigin="top left",l.style.willChange="transform",typeof process<"u"&&process.env&&process.env.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] applied scale:",{scale:i,inv:d,forceResize:u})},S=()=>{if(!c.current)return;const i=c.current;i.transform===null?l.style.removeProperty("transform"):l.style.transform=i.transform,i.transformOrigin===null?l.style.removeProperty("transform-origin"):l.style.transformOrigin=i.transformOrigin,i.width===null?l.style.removeProperty("width"):l.style.width=i.width,i.height===null?l.style.removeProperty("height"):l.style.height=i.height,i.willChange===null?l.style.removeProperty("will-change"):l.style.willChange=i.willChange,c.current=null,process.env.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] restored original styles")};let x=0;const w=()=>{cancelAnimationFrame(x),x=requestAnimationFrame(b)},f=new MutationObserver(i=>{for(const d of i)if(d.type==="attributes"&&(d.attributeName==="style"||d.attributeName==="class")){w();break}});return f.observe(document.body,{attributes:!0,attributeFilter:["style","class"]}),window.addEventListener("resize",w),w(),()=>{cancelAnimationFrame(x),f.disconnect(),window.removeEventListener("resize",w),S()}},[a,s,u])}function be(){const a=r.useRef(null);xe(a,{minDesktopWidth:1200,forceResize:!1}),r.useEffect(()=>(pe(),()=>{document.body.style.overflow=""}),[]);const[m,c]=r.useState(null),[s,u]=r.useState([]),[l,A]=r.useState(4),[b,S]=r.useState(!1),[x,w]=r.useState(1),[f,i]=r.useState(null),[d,p]=r.useState("ready"),[v,k]=r.useState(3),[z,M]=r.useState(10),[j,E]=r.useState(20),[Q,H]=r.useState(0),[L,F]=r.useState(!1),[B,q]=r.useState(""),[C,W]=r.useState(!1),[N,V]=r.useState(!1),[Y,I]=r.useState(null),[ee,G]=r.useState([]),[te,$]=r.useState([]),[y,ne]=r.useState(!1),[we,Z]=r.useState(1),[_,U]=r.useState(null);r.useEffect(()=>{const t=()=>{const g=window.visualViewport,h=g?Math.round(g.width):window.innerWidth,T=g?Math.round(g.height):window.innerHeight,R=h<768;if(ne(R),R)U(null),Z(1);else{const le=Math.min(h,T),ae=Math.max(360,Math.round(le*.97)),de=Math.min(ae,1e3);U(de),Z(1)}};t(),window.addEventListener("resize",t);const o=window.visualViewport;return o&&typeof o.addEventListener=="function"&&(o.addEventListener("resize",t),o.addEventListener("scroll",t)),()=>{window.removeEventListener("resize",t),o&&typeof o.removeEventListener=="function"&&(o.removeEventListener("resize",t),o.removeEventListener("scroll",t))}},[]);const K=t=>[...t].sort(()=>Math.random()-.5).slice(0,6),X=()=>{if(!m)return;const t=K(P[m]);u(t),S(!0),F(!1),w(1),H(0),G([]),$([]),J(t,[],!0)},J=(t=s,o=te,g=!1)=>{const h=K(t.length?t:P[m]);let T=Math.floor(Math.random()*h.length),R=h[T].name,O=0;for(;o.includes(R)&&O<20;)T=Math.floor(Math.random()*h.length),R=h[T].name,O++;u(h),i(T),$([...o,R]),W(!1),q(""),E(20),I(null),g?(p("ready"),k(3),M(10)):(p("memorize"),M(10))};r.useEffect(()=>{if(!(!b||L||C)){if(d==="ready")if(v<=0)p("memorize");else{const t=setTimeout(()=>k(o=>o-1),1e3);return()=>clearTimeout(t)}if(d==="memorize")if(z<=0)p("guess");else{const t=setTimeout(()=>M(o=>o-1),1e3);return()=>clearTimeout(t)}if(d==="guess"){if(j<=0){W(!0),I("wrong");const o=s[f].name;G(g=>[...g,{round:x,answer:B,correct:o,result:"wrong"}]);return}const t=setTimeout(()=>E(o=>o-1),1e3);return()=>clearTimeout(t)}}},[d,v,z,j,b,L,C,s,f,x,B]);const se=()=>{if(f===null||N)return;const t=s[f].name,o=B.toLowerCase().trim();let g="wrong";o===t?(H(h=>h+1),I("correct"),g="correct"):ye(o,t)===1?(H(h=>h+.5),I("almost"),g="almost"):(I("wrong"),g="wrong"),G(h=>[...h,{round:x,answer:o,correct:t,result:g}]),V(!0),setTimeout(()=>{V(!1),W(!0)},600)},oe=()=>x<l?(w(t=>t+1),J()):F(!0),re=()=>{S(!1),F(!1),c(null)},ie=r.useMemo(()=>e.jsx("div",{style:{...n.gmLogoFixed,position:"absolute",top:16,left:16,zIndex:30},children:e.jsxs("picture",{children:[e.jsx("source",{srcSet:window.origin+"/cloud/speakid/games/whatsmissing/logo.svg",type:"image/svg+xml"}),e.jsx("img",{src:window.origin+"/cloud/speakid/games/whatsmissing/logo.png",alt:"SPEAKID Logo",style:n.gmLogoImg,loading:"lazy"})]})}),[]);return e.jsx("div",{ref:a,style:{width:"100%",height:"100vh",display:"flex",justifyContent:"center",alignItems:"center",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",transition:"background 0.3s ease",overflowX:"clip"},children:e.jsx("div",{style:{width:y?"100%":_||1e3,height:y?"100%":_||1e3,display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",borderRadius:y?0:"20px",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",boxShadow:y?"none":"0 0 40px rgba(0,0,0,0.1)",margin:y?"0 auto":"unset",position:"relative"},children:e.jsx("div",{style:{transform:"none",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center"},children:e.jsxs("div",{id:"whats-missing-root",children:[!y&&ie,!m&&!b&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"WHAT'S MISSING?"}),e.jsx("p",{style:n.gmBodyM,children:"Select a theme:"}),e.jsx("div",{style:{display:"flex",gap:16},children:["animals","food","toys"].map(t=>e.jsx("button",{style:n.gmButton,onClick:()=>c(t),children:t==="animals"?"🐶 Animals":t==="food"?"🍎 Food":"🧸 Toys"},t))}),e.jsxs("div",{style:{marginTop:24},children:[e.jsx("p",{style:n.gmBodyS,children:"Choose number of rounds:"}),e.jsx("div",{style:{display:"flex",gap:12,marginTop:8},children:[3,4,5].map(t=>e.jsx("button",{style:{...n.gmButton,...l===t?n.gmButtonActive:{}},onClick:()=>A(t),children:t},t))})]})]}),m&&!b&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsxs("h1",{style:n.gmHeadline1,children:["Theme selected: ",m]}),e.jsxs("p",{style:n.gmBodyM,children:["Rounds: ",l]}),e.jsx("button",{style:n.gmButton,onClick:X,children:"▶ Start game"})]}),L&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"Results"}),e.jsxs("h2",{style:n.gmHeadline3,children:["Your score: ",Q," / ",l]}),e.jsx("p",{style:{...n.gmBodyM,color:"#10b981",marginTop:12},children:"Yahoo! You did it! 🍬✨"}),e.jsxs("table",{style:n.gmTable,children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Round"}),e.jsx("th",{children:"Your Answer"}),e.jsx("th",{children:"Correct"}),e.jsx("th",{children:"Result"})]})}),e.jsx("tbody",{children:ee.map((t,o)=>e.jsxs("tr",{children:[e.jsx("td",{style:n.gmTableCell,children:t.round}),e.jsx("td",{style:n.gmTableCell,children:t.answer||"—"}),e.jsx("td",{style:n.gmTableCell,children:t.correct}),e.jsx("td",{style:n.gmTableCell,children:t.result==="correct"?"✔ Correct":t.result==="almost"?"◐ Almost (0.5)":"✘ Wrong"})]},o))})]}),e.jsxs("div",{style:{display:"flex",gap:12,marginTop:24},children:[e.jsx("button",{style:n.gmButton,onClick:X,children:"🔁 Play again"}),e.jsx("button",{style:n.gmButton,onClick:re,children:"⬅️ Choose theme"})]})]}),b&&!L&&e.jsxs("div",{style:n.gmGameLayout,children:[e.jsxs("div",{style:{minHeight:160,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:[d==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("h1",{style:{...n.gmHeadline1,color:"#ec4c44"},children:"GET READY"}),e.jsx("div",{style:n.gmHourglass,children:"⏳"})]}),d==="memorize"&&e.jsxs("p",{style:{...n.gmBodyM,color:"#10b981"},children:["MEMORIZE (",z,")"]}),d==="guess"&&!C&&e.jsxs("p",{style:n.gmBodyM,children:["⏳ Time left: ",j,"s"]})]}),d!=="ready"&&e.jsx("div",{style:{...n.gmGrid,gridTemplateColumns:y?"repeat(2, 1fr)":"repeat(3, 210px)",gridAutoRows:y?"150px":"210px",gap:y?"12px":"20px",justifyItems:"center"},children:s.map((t,o)=>{const g=f===o&&d==="guess"&&!C;return e.jsx("div",{style:{...n.gmCard,...Y==="correct"&&f===o?n.gmCorrect:{},...Y==="wrong"&&f===o?n.gmWrong:{}},children:!g&&e.jsx("img",{src:t.src,alt:t.name,style:{width:"100%",height:"100%",objectFit:"cover"}})},o)})}),e.jsxs("div",{style:{marginTop:16,height:80},children:[d==="guess"&&!C&&e.jsxs("div",{children:[e.jsx("input",{type:"text",placeholder:"Type the missing word",value:B,onChange:t=>q(t.target.value),style:n.gmInput}),e.jsx("button",{style:{...n.gmButton,marginLeft:8},onClick:se,disabled:N,children:N?"...":"Check"})]}),C&&e.jsx("button",{type:"button",style:n.gmButton,onClick:oe,children:"Next round"})]})]})]})})})})}exports.Game=be;exports.themes=P;
|
package/dist/whats-missing.es.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsx as t, jsxs as c, Fragment as
|
|
2
|
-
import { useRef as te, useEffect as P, useState as d, useMemo as
|
|
3
|
-
const
|
|
4
|
-
src: `${
|
|
1
|
+
import { jsx as t, jsxs as c, Fragment as ue } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as te, useEffect as P, useState as d, useMemo as he, useLayoutEffect as fe } from "react";
|
|
3
|
+
const pe = window.origin + "/cloud/speakid/games/whatsmissing/", V = (l, g) => l.map(([m, o]) => ({
|
|
4
|
+
src: `${pe}${g}/${o.replace(/\s/g, "%20")}.png`,
|
|
5
5
|
name: o
|
|
6
|
-
})),
|
|
6
|
+
})), ye = [
|
|
7
7
|
["apple", "apple"],
|
|
8
8
|
["banana", "banana"],
|
|
9
9
|
["bread", "bread"],
|
|
@@ -29,7 +29,7 @@ const ue = window.origin + "/cloud/speakid/games/whatsmissing/", Y = (l, g) => l
|
|
|
29
29
|
["strawberry", "strawberry"],
|
|
30
30
|
["tomato", "tomato"],
|
|
31
31
|
["watermelon", "watermelon"]
|
|
32
|
-
],
|
|
32
|
+
], be = [
|
|
33
33
|
["bear", "bear"],
|
|
34
34
|
["cat", "cat"],
|
|
35
35
|
["crocodile", "crocodile"],
|
|
@@ -49,7 +49,7 @@ const ue = window.origin + "/cloud/speakid/games/whatsmissing/", Y = (l, g) => l
|
|
|
49
49
|
["tortoise", "tortoise"],
|
|
50
50
|
["wolf", "wolf"],
|
|
51
51
|
["zebra", "zebra"]
|
|
52
|
-
],
|
|
52
|
+
], we = [
|
|
53
53
|
["ball", "ball"],
|
|
54
54
|
["balloon", "balloon"],
|
|
55
55
|
["blocks", "blocks"],
|
|
@@ -70,10 +70,10 @@ const ue = window.origin + "/cloud/speakid/games/whatsmissing/", Y = (l, g) => l
|
|
|
70
70
|
["yo-yo", "yo-yo"],
|
|
71
71
|
["crayons", "crayons"]
|
|
72
72
|
], ee = {
|
|
73
|
-
food:
|
|
74
|
-
animals:
|
|
75
|
-
toys:
|
|
76
|
-
},
|
|
73
|
+
food: V(ye, "food"),
|
|
74
|
+
animals: V(be, "animals"),
|
|
75
|
+
toys: V(we, "toys")
|
|
76
|
+
}, xe = `
|
|
77
77
|
@keyframes spin {
|
|
78
78
|
from { transform: rotate(0deg); }
|
|
79
79
|
to { transform: rotate(360deg); }
|
|
@@ -81,15 +81,17 @@ const ue = window.origin + "/cloud/speakid/games/whatsmissing/", Y = (l, g) => l
|
|
|
81
81
|
`;
|
|
82
82
|
if (typeof document < "u" && !document.getElementById("spin-keyframes")) {
|
|
83
83
|
const l = document.createElement("style");
|
|
84
|
-
l.id = "spin-keyframes", l.innerHTML =
|
|
84
|
+
l.id = "spin-keyframes", l.innerHTML = xe, document.head.appendChild(l);
|
|
85
85
|
}
|
|
86
|
-
const
|
|
86
|
+
const ve = {
|
|
87
87
|
animation: "spin 1.4s linear infinite"
|
|
88
88
|
}, n = {
|
|
89
89
|
gmCenterScreen: {
|
|
90
90
|
position: "relative",
|
|
91
91
|
zIndex: 1,
|
|
92
|
-
|
|
92
|
+
// use percent so the component fits inside the centered square container
|
|
93
|
+
// (100vh caused overflow on tablets because it measured the viewport, not the container)
|
|
94
|
+
minHeight: "100%",
|
|
93
95
|
width: "100%",
|
|
94
96
|
display: "flex",
|
|
95
97
|
flexDirection: "column",
|
|
@@ -144,7 +146,8 @@ const be = {
|
|
|
144
146
|
position: "relative",
|
|
145
147
|
width: "100%",
|
|
146
148
|
maxWidth: "600px",
|
|
147
|
-
|
|
149
|
+
// should fill the parent square, not the full viewport
|
|
150
|
+
minHeight: "100%",
|
|
148
151
|
display: "flex",
|
|
149
152
|
flexDirection: "column",
|
|
150
153
|
alignItems: "center",
|
|
@@ -233,16 +236,16 @@ const be = {
|
|
|
233
236
|
},
|
|
234
237
|
gmHourglass: {
|
|
235
238
|
fontSize: "42px",
|
|
236
|
-
...
|
|
239
|
+
...ve
|
|
237
240
|
}
|
|
238
|
-
},
|
|
241
|
+
}, ke = () => {
|
|
239
242
|
const l = document.createElement("style");
|
|
240
243
|
l.textContent = `
|
|
241
244
|
/* scope only inside game root */
|
|
242
245
|
#whats-missing-root, #whats-missing-root * { box-sizing: border-box; }
|
|
243
246
|
#whats-missing-root img { max-width:100%; height:auto; display:block; user-select:none; }
|
|
244
247
|
`, document.head.appendChild(l);
|
|
245
|
-
},
|
|
248
|
+
}, ze = (l, g) => {
|
|
246
249
|
const m = Array.from({ length: l.length + 1 }, () => Array(g.length + 1).fill(0));
|
|
247
250
|
for (let o = 0; o <= l.length; o++) m[o][0] = o;
|
|
248
251
|
for (let o = 0; o <= g.length; o++) m[0][o] = o;
|
|
@@ -251,13 +254,13 @@ const be = {
|
|
|
251
254
|
m[o][u] = l[o - 1] === g[u - 1] ? m[o - 1][u - 1] : Math.min(m[o - 1][u - 1], m[o][u - 1], m[o - 1][u]) + 1;
|
|
252
255
|
return m[l.length][g.length];
|
|
253
256
|
};
|
|
254
|
-
function
|
|
257
|
+
function Ce(l, g = { minDesktopWidth: 1200, forceResize: !1 }) {
|
|
255
258
|
const m = te(null), o = g.minDesktopWidth ?? 1200, u = !!g.forceResize;
|
|
256
|
-
|
|
259
|
+
fe(() => {
|
|
257
260
|
if (typeof window > "u" || !l || !l.current) return;
|
|
258
|
-
const
|
|
261
|
+
const s = l.current, F = () => {
|
|
259
262
|
try {
|
|
260
|
-
const
|
|
263
|
+
const i = document.body, a = getComputedStyle(i), y = parseFloat(i.style && i.style.zoom || "");
|
|
261
264
|
if (y && !isNaN(y) && y > 0) return y;
|
|
262
265
|
const k = parseFloat(a.zoom || "");
|
|
263
266
|
if (k && !isNaN(k) && k > 0) return k;
|
|
@@ -265,120 +268,124 @@ function ve(l, g = { minDesktopWidth: 1200, forceResize: !1 }) {
|
|
|
265
268
|
if (S && S !== "none") {
|
|
266
269
|
const T = S.match(/matrix\(([^)]+)\)/);
|
|
267
270
|
if (T) {
|
|
268
|
-
const
|
|
269
|
-
if (
|
|
271
|
+
const z = T[1].split(",").map((E) => parseFloat(E))[0];
|
|
272
|
+
if (z && !isNaN(z)) return z;
|
|
270
273
|
}
|
|
271
274
|
}
|
|
272
275
|
} catch {
|
|
273
276
|
}
|
|
274
277
|
return 1;
|
|
275
|
-
},
|
|
278
|
+
}, x = () => {
|
|
276
279
|
if (o && window.innerWidth < o) {
|
|
277
|
-
|
|
280
|
+
C();
|
|
278
281
|
return;
|
|
279
282
|
}
|
|
280
|
-
const
|
|
281
|
-
if (!
|
|
282
|
-
|
|
283
|
+
const i = F();
|
|
284
|
+
if (!i || i === 1) {
|
|
285
|
+
C();
|
|
283
286
|
return;
|
|
284
287
|
}
|
|
285
288
|
m.current || (m.current = {
|
|
286
|
-
transform:
|
|
287
|
-
transformOrigin:
|
|
288
|
-
width:
|
|
289
|
-
height:
|
|
290
|
-
willChange:
|
|
289
|
+
transform: s.style.transform || null,
|
|
290
|
+
transformOrigin: s.style.transformOrigin || null,
|
|
291
|
+
width: s.style.width || null,
|
|
292
|
+
height: s.style.height || null,
|
|
293
|
+
willChange: s.style.willChange || null
|
|
291
294
|
});
|
|
292
|
-
const a = 1 /
|
|
293
|
-
u && (
|
|
294
|
-
},
|
|
295
|
+
const a = 1 / i;
|
|
296
|
+
u && (s.style.width = `${i * 100}%`, s.style.height = `${i * 100}%`), s.style.transform = `scale(${a})`, s.style.transformOrigin = "top left", s.style.willChange = "transform", typeof process < "u" && process.env && process.env.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] applied scale:", { scale: i, inv: a, forceResize: u });
|
|
297
|
+
}, C = () => {
|
|
295
298
|
if (!m.current) return;
|
|
296
|
-
const
|
|
297
|
-
|
|
299
|
+
const i = m.current;
|
|
300
|
+
i.transform === null ? s.style.removeProperty("transform") : s.style.transform = i.transform, i.transformOrigin === null ? s.style.removeProperty("transform-origin") : s.style.transformOrigin = i.transformOrigin, i.width === null ? s.style.removeProperty("width") : s.style.width = i.width, i.height === null ? s.style.removeProperty("height") : s.style.height = i.height, i.willChange === null ? s.style.removeProperty("will-change") : s.style.willChange = i.willChange, m.current = null, process.env.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] restored original styles");
|
|
298
301
|
};
|
|
299
|
-
let
|
|
302
|
+
let w = 0;
|
|
300
303
|
const v = () => {
|
|
301
|
-
cancelAnimationFrame(
|
|
302
|
-
},
|
|
303
|
-
for (const a of
|
|
304
|
+
cancelAnimationFrame(w), w = requestAnimationFrame(x);
|
|
305
|
+
}, p = new MutationObserver((i) => {
|
|
306
|
+
for (const a of i)
|
|
304
307
|
if (a.type === "attributes" && (a.attributeName === "style" || a.attributeName === "class")) {
|
|
305
308
|
v();
|
|
306
309
|
break;
|
|
307
310
|
}
|
|
308
311
|
});
|
|
309
|
-
return
|
|
310
|
-
cancelAnimationFrame(
|
|
312
|
+
return p.observe(document.body, { attributes: !0, attributeFilter: ["style", "class"] }), window.addEventListener("resize", v), v(), () => {
|
|
313
|
+
cancelAnimationFrame(w), p.disconnect(), window.removeEventListener("resize", v), C();
|
|
311
314
|
};
|
|
312
315
|
}, [l, o, u]);
|
|
313
316
|
}
|
|
314
|
-
function
|
|
317
|
+
function Me() {
|
|
315
318
|
const l = te(null);
|
|
316
|
-
|
|
319
|
+
Ce(l, { minDesktopWidth: 1200, forceResize: !1 }), P(() => (ke(), () => {
|
|
317
320
|
document.body.style.overflow = "";
|
|
318
321
|
}), []);
|
|
319
|
-
const [g, m] = d(null), [o, u] = d([]), [
|
|
322
|
+
const [g, m] = d(null), [o, u] = d([]), [s, F] = d(4), [x, C] = d(!1), [w, v] = d(1), [p, i] = d(null), [a, y] = d("ready"), [k, S] = d(3), [T, B] = d(10), [z, E] = d(20), [ne, W] = d(0), [A, N] = d(!1), [H, Y] = d(""), [R, j] = d(!1), [D, $] = d(!1), [q, L] = d(null), [oe, G] = d([]), [re, Z] = d([]), [b, ie] = d(!1), [Se, _] = d(1), [U, K] = d(null);
|
|
320
323
|
P(() => {
|
|
321
324
|
const e = () => {
|
|
322
|
-
const
|
|
323
|
-
if (ie(
|
|
325
|
+
const h = window.visualViewport, f = h ? Math.round(h.width) : window.innerWidth, I = h ? Math.round(h.height) : window.innerHeight, M = f < 768;
|
|
326
|
+
if (ie(M), M)
|
|
324
327
|
K(null), _(1);
|
|
325
328
|
else {
|
|
326
|
-
const
|
|
327
|
-
K(
|
|
329
|
+
const ce = Math.min(f, I), me = Math.max(360, Math.round(ce * 0.97)), ge = Math.min(me, 1e3);
|
|
330
|
+
K(ge), _(1);
|
|
328
331
|
}
|
|
329
332
|
};
|
|
330
|
-
|
|
333
|
+
e(), window.addEventListener("resize", e);
|
|
334
|
+
const r = window.visualViewport;
|
|
335
|
+
return r && typeof r.addEventListener == "function" && (r.addEventListener("resize", e), r.addEventListener("scroll", e)), () => {
|
|
336
|
+
window.removeEventListener("resize", e), r && typeof r.removeEventListener == "function" && (r.removeEventListener("resize", e), r.removeEventListener("scroll", e));
|
|
337
|
+
};
|
|
331
338
|
}, []);
|
|
332
339
|
const X = (e) => [...e].sort(() => Math.random() - 0.5).slice(0, 6), J = () => {
|
|
333
340
|
if (!g) return;
|
|
334
341
|
const e = X(ee[g]);
|
|
335
|
-
u(e),
|
|
336
|
-
}, Q = (e = o,
|
|
337
|
-
const
|
|
338
|
-
let I = Math.floor(Math.random() *
|
|
339
|
-
for (;
|
|
340
|
-
I = Math.floor(Math.random() *
|
|
341
|
-
u(
|
|
342
|
+
u(e), C(!0), N(!1), v(1), W(0), G([]), Z([]), Q(e, [], !0);
|
|
343
|
+
}, Q = (e = o, r = re, h = !1) => {
|
|
344
|
+
const f = X(e.length ? e : ee[g]);
|
|
345
|
+
let I = Math.floor(Math.random() * f.length), M = f[I].name, O = 0;
|
|
346
|
+
for (; r.includes(M) && O < 20; )
|
|
347
|
+
I = Math.floor(Math.random() * f.length), M = f[I].name, O++;
|
|
348
|
+
u(f), i(I), Z([...r, M]), j(!1), Y(""), E(20), L(null), h ? (y("ready"), S(3), B(10)) : (y("memorize"), B(10));
|
|
342
349
|
};
|
|
343
350
|
P(() => {
|
|
344
|
-
if (!(!
|
|
351
|
+
if (!(!x || A || R)) {
|
|
345
352
|
if (a === "ready")
|
|
346
353
|
if (k <= 0) y("memorize");
|
|
347
354
|
else {
|
|
348
|
-
const e = setTimeout(() => S((
|
|
355
|
+
const e = setTimeout(() => S((r) => r - 1), 1e3);
|
|
349
356
|
return () => clearTimeout(e);
|
|
350
357
|
}
|
|
351
358
|
if (a === "memorize")
|
|
352
359
|
if (T <= 0) y("guess");
|
|
353
360
|
else {
|
|
354
|
-
const e = setTimeout(() =>
|
|
361
|
+
const e = setTimeout(() => B((r) => r - 1), 1e3);
|
|
355
362
|
return () => clearTimeout(e);
|
|
356
363
|
}
|
|
357
364
|
if (a === "guess") {
|
|
358
|
-
if (
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
...
|
|
363
|
-
{ round:
|
|
365
|
+
if (z <= 0) {
|
|
366
|
+
j(!0), L("wrong");
|
|
367
|
+
const r = o[p].name;
|
|
368
|
+
G((h) => [
|
|
369
|
+
...h,
|
|
370
|
+
{ round: w, answer: H, correct: r, result: "wrong" }
|
|
364
371
|
]);
|
|
365
372
|
return;
|
|
366
373
|
}
|
|
367
|
-
const e = setTimeout(() =>
|
|
374
|
+
const e = setTimeout(() => E((r) => r - 1), 1e3);
|
|
368
375
|
return () => clearTimeout(e);
|
|
369
376
|
}
|
|
370
377
|
}
|
|
371
|
-
}, [a, k, T,
|
|
378
|
+
}, [a, k, T, z, x, A, R, o, p, w, H]);
|
|
372
379
|
const se = () => {
|
|
373
|
-
if (
|
|
374
|
-
const e = o[
|
|
375
|
-
let
|
|
376
|
-
|
|
377
|
-
|
|
380
|
+
if (p === null || D) return;
|
|
381
|
+
const e = o[p].name, r = H.toLowerCase().trim();
|
|
382
|
+
let h = "wrong";
|
|
383
|
+
r === e ? (W((f) => f + 1), L("correct"), h = "correct") : ze(r, e) === 1 ? (W((f) => f + 0.5), L("almost"), h = "almost") : (L("wrong"), h = "wrong"), G((f) => [...f, { round: w, answer: r, correct: e, result: h }]), $(!0), setTimeout(() => {
|
|
384
|
+
$(!1), j(!0);
|
|
378
385
|
}, 600);
|
|
379
|
-
}, le = () =>
|
|
380
|
-
|
|
381
|
-
}, de =
|
|
386
|
+
}, le = () => w < s ? (v((e) => e + 1), Q()) : N(!0), ae = () => {
|
|
387
|
+
C(!1), N(!1), m(null);
|
|
388
|
+
}, de = he(
|
|
382
389
|
() => (
|
|
383
390
|
// ensure logo is positioned inside the square container
|
|
384
391
|
/* @__PURE__ */ t("div", { style: { ...n.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }, children: /* @__PURE__ */ c("picture", { children: [
|
|
@@ -432,7 +439,7 @@ function Se() {
|
|
|
432
439
|
},
|
|
433
440
|
children: /* @__PURE__ */ c("div", { id: "whats-missing-root", children: [
|
|
434
441
|
!b && de,
|
|
435
|
-
!g && !
|
|
442
|
+
!g && !x && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
|
|
436
443
|
/* @__PURE__ */ t("h1", { style: n.gmHeadline1, children: "WHAT'S MISSING?" }),
|
|
437
444
|
/* @__PURE__ */ t("p", { style: n.gmBodyM, children: "Select a theme:" }),
|
|
438
445
|
/* @__PURE__ */ t("div", { style: { display: "flex", gap: 16 }, children: ["animals", "food", "toys"].map((e) => /* @__PURE__ */ t("button", { style: n.gmButton, onClick: () => m(e), children: e === "animals" ? "🐶 Animals" : e === "food" ? "🍎 Food" : "🧸 Toys" }, e)) }),
|
|
@@ -443,33 +450,33 @@ function Se() {
|
|
|
443
450
|
{
|
|
444
451
|
style: {
|
|
445
452
|
...n.gmButton,
|
|
446
|
-
...
|
|
453
|
+
...s === e ? n.gmButtonActive : {}
|
|
447
454
|
},
|
|
448
|
-
onClick: () =>
|
|
455
|
+
onClick: () => F(e),
|
|
449
456
|
children: e
|
|
450
457
|
},
|
|
451
458
|
e
|
|
452
459
|
)) })
|
|
453
460
|
] })
|
|
454
461
|
] }),
|
|
455
|
-
g && !
|
|
462
|
+
g && !x && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
|
|
456
463
|
/* @__PURE__ */ c("h1", { style: n.gmHeadline1, children: [
|
|
457
464
|
"Theme selected: ",
|
|
458
465
|
g
|
|
459
466
|
] }),
|
|
460
467
|
/* @__PURE__ */ c("p", { style: n.gmBodyM, children: [
|
|
461
468
|
"Rounds: ",
|
|
462
|
-
|
|
469
|
+
s
|
|
463
470
|
] }),
|
|
464
471
|
/* @__PURE__ */ t("button", { style: n.gmButton, onClick: J, children: "▶ Start game" })
|
|
465
472
|
] }),
|
|
466
|
-
|
|
473
|
+
A && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
|
|
467
474
|
/* @__PURE__ */ t("h1", { style: n.gmHeadline1, children: "Results" }),
|
|
468
475
|
/* @__PURE__ */ c("h2", { style: n.gmHeadline3, children: [
|
|
469
476
|
"Your score: ",
|
|
470
477
|
ne,
|
|
471
478
|
" / ",
|
|
472
|
-
|
|
479
|
+
s
|
|
473
480
|
] }),
|
|
474
481
|
/* @__PURE__ */ t("p", { style: { ...n.gmBodyM, color: "#10b981", marginTop: 12 }, children: "Yahoo! You did it! 🍬✨" }),
|
|
475
482
|
/* @__PURE__ */ c("table", { style: n.gmTable, children: [
|
|
@@ -479,19 +486,19 @@ function Se() {
|
|
|
479
486
|
/* @__PURE__ */ t("th", { children: "Correct" }),
|
|
480
487
|
/* @__PURE__ */ t("th", { children: "Result" })
|
|
481
488
|
] }) }),
|
|
482
|
-
/* @__PURE__ */ t("tbody", { children: oe.map((e,
|
|
489
|
+
/* @__PURE__ */ t("tbody", { children: oe.map((e, r) => /* @__PURE__ */ c("tr", { children: [
|
|
483
490
|
/* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.round }),
|
|
484
491
|
/* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.answer || "—" }),
|
|
485
492
|
/* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.correct }),
|
|
486
493
|
/* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.result === "correct" ? "✔ Correct" : e.result === "almost" ? "◐ Almost (0.5)" : "✘ Wrong" })
|
|
487
|
-
] },
|
|
494
|
+
] }, r)) })
|
|
488
495
|
] }),
|
|
489
496
|
/* @__PURE__ */ c("div", { style: { display: "flex", gap: 12, marginTop: 24 }, children: [
|
|
490
497
|
/* @__PURE__ */ t("button", { style: n.gmButton, onClick: J, children: "🔁 Play again" }),
|
|
491
498
|
/* @__PURE__ */ t("button", { style: n.gmButton, onClick: ae, children: "⬅️ Choose theme" })
|
|
492
499
|
] })
|
|
493
500
|
] }),
|
|
494
|
-
|
|
501
|
+
x && !A && /* @__PURE__ */ c("div", { style: n.gmGameLayout, children: [
|
|
495
502
|
/* @__PURE__ */ c(
|
|
496
503
|
"div",
|
|
497
504
|
{
|
|
@@ -503,7 +510,7 @@ function Se() {
|
|
|
503
510
|
alignItems: "center"
|
|
504
511
|
},
|
|
505
512
|
children: [
|
|
506
|
-
a === "ready" && /* @__PURE__ */ c(
|
|
513
|
+
a === "ready" && /* @__PURE__ */ c(ue, { children: [
|
|
507
514
|
/* @__PURE__ */ t("h1", { style: { ...n.gmHeadline1, color: "#ec4c44" }, children: "GET READY" }),
|
|
508
515
|
/* @__PURE__ */ t("div", { style: n.gmHourglass, children: "⏳" })
|
|
509
516
|
] }),
|
|
@@ -514,7 +521,7 @@ function Se() {
|
|
|
514
521
|
] }),
|
|
515
522
|
a === "guess" && !R && /* @__PURE__ */ c("p", { style: n.gmBodyM, children: [
|
|
516
523
|
"⏳ Time left: ",
|
|
517
|
-
|
|
524
|
+
z,
|
|
518
525
|
"s"
|
|
519
526
|
] })
|
|
520
527
|
]
|
|
@@ -530,17 +537,17 @@ function Se() {
|
|
|
530
537
|
gap: b ? "12px" : "20px",
|
|
531
538
|
justifyItems: "center"
|
|
532
539
|
},
|
|
533
|
-
children: o.map((e,
|
|
534
|
-
const
|
|
540
|
+
children: o.map((e, r) => {
|
|
541
|
+
const h = p === r && a === "guess" && !R;
|
|
535
542
|
return /* @__PURE__ */ t(
|
|
536
543
|
"div",
|
|
537
544
|
{
|
|
538
545
|
style: {
|
|
539
546
|
...n.gmCard,
|
|
540
|
-
...
|
|
541
|
-
...
|
|
547
|
+
...q === "correct" && p === r ? n.gmCorrect : {},
|
|
548
|
+
...q === "wrong" && p === r ? n.gmWrong : {}
|
|
542
549
|
},
|
|
543
|
-
children: !
|
|
550
|
+
children: !h && /* @__PURE__ */ t(
|
|
544
551
|
"img",
|
|
545
552
|
{
|
|
546
553
|
src: e.src,
|
|
@@ -553,7 +560,7 @@ function Se() {
|
|
|
553
560
|
}
|
|
554
561
|
)
|
|
555
562
|
},
|
|
556
|
-
|
|
563
|
+
r
|
|
557
564
|
);
|
|
558
565
|
})
|
|
559
566
|
}
|
|
@@ -565,8 +572,8 @@ function Se() {
|
|
|
565
572
|
{
|
|
566
573
|
type: "text",
|
|
567
574
|
placeholder: "Type the missing word",
|
|
568
|
-
value:
|
|
569
|
-
onChange: (e) =>
|
|
575
|
+
value: H,
|
|
576
|
+
onChange: (e) => Y(e.target.value),
|
|
570
577
|
style: n.gmInput
|
|
571
578
|
}
|
|
572
579
|
),
|
|
@@ -575,8 +582,8 @@ function Se() {
|
|
|
575
582
|
{
|
|
576
583
|
style: { ...n.gmButton, marginLeft: 8 },
|
|
577
584
|
onClick: se,
|
|
578
|
-
disabled:
|
|
579
|
-
children:
|
|
585
|
+
disabled: D,
|
|
586
|
+
children: D ? "..." : "Check"
|
|
580
587
|
}
|
|
581
588
|
)
|
|
582
589
|
] }),
|
|
@@ -592,6 +599,6 @@ function Se() {
|
|
|
592
599
|
);
|
|
593
600
|
}
|
|
594
601
|
export {
|
|
595
|
-
|
|
602
|
+
Me as Game,
|
|
596
603
|
ee as themes
|
|
597
604
|
};
|
package/package.json
CHANGED
package/src/Game.styles.ts
CHANGED
|
@@ -25,7 +25,9 @@ export const styles: Record<string, CSSProperties> = {
|
|
|
25
25
|
gmCenterScreen: {
|
|
26
26
|
position: "relative",
|
|
27
27
|
zIndex: 1,
|
|
28
|
-
|
|
28
|
+
// use percent so the component fits inside the centered square container
|
|
29
|
+
// (100vh caused overflow on tablets because it measured the viewport, not the container)
|
|
30
|
+
minHeight: "100%",
|
|
29
31
|
width: "100%",
|
|
30
32
|
display: "flex",
|
|
31
33
|
flexDirection: "column",
|
|
@@ -100,7 +102,8 @@ export const styles: Record<string, CSSProperties> = {
|
|
|
100
102
|
position: "relative",
|
|
101
103
|
width: "100%",
|
|
102
104
|
maxWidth: "600px",
|
|
103
|
-
|
|
105
|
+
// should fill the parent square, not the full viewport
|
|
106
|
+
minHeight: "100%",
|
|
104
107
|
display: "flex",
|
|
105
108
|
flexDirection: "column",
|
|
106
109
|
alignItems: "center",
|
|
@@ -203,7 +206,8 @@ export const styles: Record<string, CSSProperties> = {
|
|
|
203
206
|
flexDirection: "column",
|
|
204
207
|
alignItems: "center",
|
|
205
208
|
justifyContent: "center",
|
|
206
|
-
|
|
209
|
+
// use percent so the wrapper scales with the container square
|
|
210
|
+
height: "60%",
|
|
207
211
|
gap: "16px",
|
|
208
212
|
},
|
|
209
213
|
|
package/src/Game.tsx
CHANGED
|
@@ -213,34 +213,54 @@ export default function Game() {
|
|
|
213
213
|
|
|
214
214
|
// ✅ адаптив под мобилки, планшеты и десктоп
|
|
215
215
|
useEffect(() => {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
216
|
+
const resize = () => {
|
|
217
|
+
// Prefer visualViewport when available — it's more accurate under page zoom
|
|
218
|
+
const vv = (window as any).visualViewport;
|
|
219
|
+
const vw = vv ? Math.round(vv.width) : window.innerWidth;
|
|
220
|
+
const vh = vv ? Math.round(vv.height) : window.innerHeight;
|
|
221
|
+
|
|
222
|
+
const mobile = vw < 768;
|
|
223
|
+
// tablet flag kept for possible future use
|
|
224
|
+
const tablet = vw >= 768 && vw < 1200;
|
|
225
|
+
|
|
226
|
+
// treat only phones as "mobile" so tablets use the desktop (square/centered) layout
|
|
227
|
+
setIsMobile(mobile);
|
|
228
|
+
|
|
229
|
+
if (mobile) {
|
|
230
|
+
// mobile: fluid full-width layout
|
|
231
|
+
setContainerSize(null);
|
|
232
|
+
setScale(1);
|
|
233
|
+
} else {
|
|
234
|
+
// tablets and desktops: fit into a centered square (max 1000)
|
|
235
|
+
const safeFactor = 0.97;
|
|
236
|
+
const maxSquare = 1000;
|
|
237
|
+
const available = Math.min(vw, vh);
|
|
238
|
+
const sizePx = Math.max(360, Math.round(available * safeFactor)); // don't go too small
|
|
239
|
+
const finalSize = Math.min(sizePx, maxSquare);
|
|
240
|
+
setContainerSize(finalSize);
|
|
241
|
+
setScale(1);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// run once
|
|
246
|
+
resize();
|
|
247
|
+
|
|
248
|
+
// listen to both window and visualViewport (if available) since some browsers update only visualViewport on zoom
|
|
249
|
+
window.addEventListener("resize", resize);
|
|
250
|
+
const vv = (window as any).visualViewport;
|
|
251
|
+
if (vv && typeof vv.addEventListener === "function") {
|
|
252
|
+
vv.addEventListener("resize", resize);
|
|
253
|
+
vv.addEventListener("scroll", resize);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return () => {
|
|
257
|
+
window.removeEventListener("resize", resize);
|
|
258
|
+
if (vv && typeof vv.removeEventListener === "function") {
|
|
259
|
+
vv.removeEventListener("resize", resize);
|
|
260
|
+
vv.removeEventListener("scroll", resize);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}, []);
|
|
244
264
|
|
|
245
265
|
|
|
246
266
|
const getRandomSix = (arr: ImageItem[]) =>
|