UNPKG

mycrossword

Version:
1,750 lines 59.4 kB
import { jsx as u, jsxs as G, Fragment as ee } from "react/jsx-runtime"; import * as C from "react"; import le, { useLayoutEffect as Me, useEffect as ae, useRef as Ne, useCallback as oe, useState as ye } from "react"; function Be(e) { return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e; } var Ie = { exports: {} }; /*! Copyright (c) 2018 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ (function(e) { (function() { var t = {}.hasOwnProperty; function n() { for (var i = "", r = 0; r < arguments.length; r++) { var c = arguments[r]; c && (i = s(i, o(c))); } return i; } function o(i) { if (typeof i == "string" || typeof i == "number") return i; if (typeof i != "object") return ""; if (Array.isArray(i)) return n.apply(null, i); if (i.toString !== Object.prototype.toString && !i.toString.toString().includes("[native code]")) return i.toString(); var r = ""; for (var c in i) t.call(i, c) && i[c] && (r = s(r, c)); return r; } function s(i, r) { return r ? i ? i + " " + r : i + r : i; } e.exports ? (n.default = n, e.exports = n) : window.classNames = n; })(); })(Ie); var We = Ie.exports; const de = /* @__PURE__ */ Be(We); function B(e) { return (...t) => de(t); } const Oe = ["b", "strong", "i", "em", "sub", "sup"], Pe = /[A-Z]/; function Ae(e, t) { return e.length !== 1 ? !1 : e.match(t); } function je(e) { return e.top >= 0 && e.left >= 0 && e.right <= (window.innerWidth || document.documentElement.clientWidth) && e.bottom <= (window.innerHeight || document.documentElement.clientHeight); } function Ue(e, t) { return e.top >= t.top && e.left >= t.left && e.right <= t.right && e.bottom <= t.bottom; } function $e(e, t, n = "") { return { value: new Array(e).fill(n).map(() => new Array(t).fill(n)) }; } function Ge(e, t, n) { const o = $e(e, t); return n.forEach(({ guess: s, pos: i }) => { o.value[i.col][i.row] = s !== void 0 ? s : ""; }), o; } function Ve(e, t, n, o) { if (e.value.reduce((i, r) => i + r.length, 0) !== t * n) return !1; for (let i = 0; i < t; i += 1) for (let r = 0; r < n; r += 1) { const c = e.value[i][r]; if (c !== "" && !Ae(c, o)) return !1; } return !0; } const ge = (e) => { let t; const n = /* @__PURE__ */ new Set(), o = (f, p) => { const d = typeof f == "function" ? f(t) : f; if (!Object.is(d, t)) { const m = t; t = p ?? (typeof d != "object" || d === null) ? d : Object.assign({}, t, d), n.forEach((w) => w(t, m)); } }, s = () => t, c = { setState: o, getState: s, getInitialState: () => h, subscribe: (f) => (n.add(f), () => n.delete(f)) }, h = t = e(o, s, c); return c; }, Fe = (e) => e ? ge(e) : ge, Ke = (e) => e; function qe(e, t = Ke) { const n = le.useSyncExternalStore( e.subscribe, le.useCallback(() => t(e.getState()), [e, t]), le.useCallback(() => t(e.getInitialState()), [e, t]) ); return le.useDebugValue(n), n; } const we = (e) => { const t = Fe(e), n = (o) => qe(t, o); return Object.assign(n, t), n; }, Re = (e) => e ? we(e) : we, Y = Re((e) => ({ clues: [], setClues: (t) => { e(() => ({ clues: t })); }, select: (t) => { e((n) => (typeof window < "u" && window.history.replaceState(null, "", `#${t}`), { clues: n.clues.map((o) => ({ ...o, selected: o.id === t })) })); }, answerAll: (t) => { e((n) => ({ clues: n.clues.map((o) => ({ ...o, answered: t })) })); }, answerSome: (t, n) => { e((o) => ({ clues: o.clues.map((s) => t.includes(s.id) ? { ...s, answered: n } : s) })); } })), V = Re((e, t) => ({ cells: [], complete: !1, checkComplete: () => { if (t().complete) return null; const n = t().cells.every((o) => o.val === o.guess); return e({ complete: n }), n; }, resetComplete: () => { e({ complete: !1 }); }, setCells: (n) => { e((o) => ({ cells: n.map((s, i) => { const r = o.cells[i]; return r && r.guess !== s.guess ? { ...s, checked: !1 } : s; }) })); }, select: (n) => { e((o) => { const s = o.cells.find((r) => r.selected), i = s === void 0 || s.pos.col !== n.col || s.pos.row !== n.row; return { cells: o.cells.map((r) => { const c = r.val === r.guess; return { ...r, selected: r.pos.col === n.col && r.pos.row === n.row, checked: i && c ? !1 : r.checked }; }) }; }); }, answerAll: (n) => { e((o) => ({ cells: o.cells.map((s) => ({ ...s, guess: n ? s.val : void 0, checked: !1 })) })); } })); function Je({ message: e, style: t }) { const n = B(); return /* @__PURE__ */ u("div", { className: n("GridError"), style: t, children: /* @__PURE__ */ G("div", { role: "alert", children: [ /* @__PURE__ */ u("h1", { className: n("GridError__title"), children: "Something went wrong" }), /* @__PURE__ */ u("p", { className: n("GridError__subTitle"), children: e }) ] }) }); } function Ye({ className: e }) { return /* @__PURE__ */ u( "svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", className: e, viewBox: "0 0 16 16", children: /* @__PURE__ */ u( "path", { fillRule: "evenodd", d: "M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z" } ) } ); } function Xe({ className: e }) { return /* @__PURE__ */ u( "svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", className: e, viewBox: "0 0 16 16", children: /* @__PURE__ */ u( "path", { fillRule: "evenodd", d: "M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z" } ) } ); } function se(e, t = {}) { const { allowedTags: n = [] } = t; if (!/<|>/.test(e)) return e; let o = "", s = e; const i = n.length > 0 ? `\\b(${n.join("|")})\\b` : "(?!)", r = new RegExp(`</?(?!${i})\\w+[^>]*>`, "gi"), c = /<(\w+)[^>]*>/gi; for (; o !== s; ) o = s, s = s.replace(r, ""), s = s.replace(c, "<$1>"); return s; } const Ze = { "&amp;": "&", "&lt;": "<", "&gt;": ">", "&quot;": '"', "&#39;": "'", "&#x27;": "'", "&#x2F;": "/", "&#96;": "`", "&#x3D;": "=", "&nbsp;": " ", "&copy;": "©", "&reg;": "®", "&trade;": "™", "&euro;": "€", "&pound;": "£", "&yen;": "¥", "&cent;": "¢", "&apos;": "'", "&sect;": "§", "&para;": "¶", "&plusmn;": "±", "&times;": "×", "&divide;": "÷", "&laquo;": "«", "&raquo;": "»", "&ldquo;": "“", "&rdquo;": "”", "&lsquo;": "‘", "&rsquo;": "’", "&hellip;": "…", "&middot;": "·", "&bull;": "•", "&ndash;": "–", "&mdash;": "—", "&alpha;": "α", "&beta;": "β", "&gamma;": "γ", "&delta;": "δ", "&pi;": "π", "&sigma;": "σ", "&omega;": "ω", "&mu;": "μ", "&tau;": "τ", "&phi;": "φ", "&chi;": "χ", "&psi;": "ψ", "&theta;": "θ" }; function re(e) { return e.replace(/&([^;]+);/g, (t, n) => { const o = Ze[t.toLowerCase()]; if (o !== void 0) return o; if (n.startsWith("#")) { let s; if (n.startsWith("#x") || n.startsWith("#X") ? s = parseInt(n.slice(2), 16) : s = parseInt(n.slice(1), 10), !isNaN(s)) try { return String.fromCodePoint(s); } catch { return t; } } return t; }); } function Qe({ allowedTags: e, num: t, onMoveNext: n, onMovePrev: o, show: s, text: i }) { const r = B(); return /* @__PURE__ */ u("div", { className: r("StickyClue", `StickyClue--${s}`), children: i !== void 0 && t !== void 0 ? /* @__PURE__ */ G(ee, { children: [ /* @__PURE__ */ u( "button", { "aria-label": "Previous clue", className: r("StickyClue__button"), onClick: o, type: "button", children: /* @__PURE__ */ u(Ye, {}) } ), /* @__PURE__ */ u("div", { className: r("StickyClue__inner"), children: /* @__PURE__ */ G("span", { className: r("StickyClue__text"), children: [ /* @__PURE__ */ u("span", { className: r("StickyClue__num"), children: t }), /* @__PURE__ */ u( "span", { dangerouslySetInnerHTML: { __html: se(re(i), { allowedTags: e }) } } ) ] }) }), /* @__PURE__ */ u( "button", { "aria-label": "Next clue", className: r("StickyClue__button"), onClick: n, type: "button", children: /* @__PURE__ */ u(Xe, {}) } ) ] }) : null }); } const Le = (e, t) => { const n = 1 + (e + 1) * t.col, o = 1 + (e + 1) * t.row, s = n + 1, i = o + Math.ceil(e * 0.29), r = n + Math.ceil(e * 0.5), c = o + Math.ceil(e * 0.675); return { xRect: n, yRect: o, xNum: s, yNum: i, xText: r, yText: c }; }; function ze({ cellSize: e, clueIds: t, guess: n, inputRef: o, highlighted: s, selected: i, num: r, onCellFocus: c, pos: h, selectedClueIndex: f, checked: p, val: d }) { if (t.length !== 1 && t.length !== 2) throw new Error( "Crossword data error: cell does not have 1 or 2 directions" ); const m = B(), w = Y((D) => D.select), l = V((D) => D.select), { xRect: a, yRect: I, xNum: L, yNum: _, xText: R, yText: W } = Le( e, h ), O = (D, M) => { c !== void 0 && c({ pos: D, clueId: M }); }, $ = () => { var j; let D = f === -1 ? 0 : f; t.length === 2 && i && (D = f === 0 ? 1 : 0); const M = t[D]; w(M), i || l(h), (!i || t.length === 2) && O(h, M), (j = o == null ? void 0 : o.current) == null || j.focus({ preventScroll: !0 }); }; return /* @__PURE__ */ G( "g", { className: m( "GridCell", s ? "GridCell--highlighted" : null, i ? "GridCell--selected" : null, p ? n === d ? "GridCell--correct" : "GridCell--incorrect" : null ), onClick: $, children: [ /* @__PURE__ */ u( "rect", { className: m("GridCell__rect"), x: a, y: I, width: e, height: e } ), r ? /* @__PURE__ */ u( "text", { className: m("GridCell__num"), x: L, y: _, style: { fontSize: Math.ceil(e * 0.32) }, children: r } ) : null, p && n !== d ? /* @__PURE__ */ u( "line", { className: m("GridCell__strikethrough"), x1: a + e, y1: I, x2: a, y2: I + e } ) : null, /* @__PURE__ */ u( "text", { className: m("GridCell__text"), textAnchor: "middle", x: R, y: W, style: { fontSize: Math.ceil(e * 0.55) }, children: n } ) ] } ); } const et = C.memo(ze); const tt = C.forwardRef( ({ onChange: e, onKeyDown: t, visible: n }, o) => { const s = B(); return /* @__PURE__ */ u( "input", { autoComplete: "off", autoCorrect: "off", autoFocus: !1, className: s( "GridInput", n ? null : "GridInput--inclusivelyHidden" ), maxLength: 1, onChange: e, onKeyDown: t, ref: o, spellCheck: "false", tabIndex: -1, type: "text", value: "" } ); } ), nt = tt; function ot(e, t) { const [n, o] = C.useState(e); return C.useEffect(() => { const s = setTimeout(() => { o(e); }, t); return () => { clearTimeout(s); }; }, [e, t]), n; } function ve(e, t) { return e * (t + 1) + 1; } function st({ char: e, col: t, row: n, direction: o, cellSize: s }) { const i = ve(n, s), r = ve(t, s), c = o === "across"; if (e === ",") { const h = c ? 1 : s, f = c ? s : 1, p = c ? r - 2 : r, d = c ? i : i - 2; return /* @__PURE__ */ u("rect", { width: h, height: f, x: p, y: d }); } if (e === "-") { const h = c ? s * 0.25 : 1, f = c ? 1 : s * 0.25, p = c ? r - 0.5 - h * 0.5 : r + s * 0.5 + h * 0.5, d = c ? i + s * 0.5 + f * 0.5 : i - 0.5 - f * 0.5; return /* @__PURE__ */ u("rect", { width: h, height: f, x: p, y: d }); } return /* @__PURE__ */ u(ee, {}); } function Ce(e, t, n, o) { if (t <= 0 || t >= n.length) return null; const s = n.position.x + (n.direction === "across" ? t : 0), i = n.position.y + (n.direction === "across" ? 0 : t); return /* @__PURE__ */ u( st, { cellSize: o, char: e, direction: n.direction, col: s, row: i }, [s, i, n.direction].join(",") ); } function rt({ clues: e, cellSize: t }) { return /* @__PURE__ */ u("svg", { className: "GridSeparators", children: e.filter((n) => Object.keys(n.separatorLocations).length > 0).map((n) => { const o = [], s = n.separatorLocations[","], i = n.separatorLocations["-"]; return s !== void 0 && o.push( s.map((r) => Ce(",", r, n, t)) ), i !== void 0 && o.push( i.map((r) => Ce("-", r, n, t)) ), o; }) }); } const it = C.memo(rt); function ue({ cells: e, newCell: t, when: n }) { return e.map((o) => o.pos.col === t.pos.col && o.pos.row === t.pos.row && (n === void 0 || n(o)) ? t : o); } function ct({ cols: e, rows: t, entries: n, guessGrid: o, allowMissingSolutions: s = !1 }) { const i = [], r = n.map((c) => c.id); return n.forEach((c) => { var h, f; for (let p = 0; p < c.length; p += 1) { const d = c.direction === "across", m = d ? c.position.x + p : c.position.x, w = d ? c.position.y : c.position.y + p; if (m < 0 || m >= e || w < 0 || w >= t) throw new Error("Crossword data error: out of bounds"); if (!s && c.solution !== void 0 && c.length !== c.solution.length) throw new Error("Crossword data error: solution length mismatch"); if (!c.group.includes(c.id)) throw new Error("Crossword data error: clue id missing from group"); if (!c.group.every((a) => r.includes(a))) throw new Error("Crossword data error: group clue id not found"); const l = i.find( ({ pos: a }) => a.col === m && a.row === w ); if (l === void 0) { const a = o == null ? void 0 : o.value[m][w], I = { clueIds: [c.id], guess: a !== "" ? a : void 0, num: p === 0 ? c.number : void 0, pos: { col: m, row: w }, selected: !1, val: (h = c.solution) == null ? void 0 : h[p] }; i.push(I); } else { if (d && l.clueIds.some((I) => I.endsWith("across"))) throw new Error("Crossword data error: overlapping across solutions"); if (!d && l.clueIds.some((I) => I.endsWith("down"))) throw new Error("Crossword data error: overlapping down solutions"); const a = (f = c.solution) == null ? void 0 : f[p]; if (!s && l.val !== a) throw new Error("Crossword data error: solution character clash"); l.num = p === 0 ? c.number : l.num, l.clueIds = [...l.clueIds, c.id], s && a !== void 0 && (l.val = a); } } }), i; } function be(e, t) { return e.find( (n) => n.pos.col === t.col && n.pos.row === t.row ); } function lt(e, t, n) { const o = be(e, { col: t.pos.col - (n ? 0 : 1), row: t.pos.row - (n ? 1 : 0) }), s = be(e, { col: t.pos.col + (n ? 0 : 1), row: t.pos.row + (n ? 1 : 0) }); return (o == null ? void 0 : o.guess) === void 0 && (s == null ? void 0 : s.guess) === void 0; } function ie(e, t) { const n = []; return e.forEach((o) => { const s = t.filter((i) => i.clueIds.includes(o)).sort( (i, r) => i.pos.col - r.pos.col || i.pos.row - r.pos.row ); n.push(...s); }), n; } function ut(e, t) { const n = { ",": [], "-": [] }; let o = 0; return e.forEach((s) => { var r, c; const i = t.find((h) => h.id === s); if (i !== void 0) { const h = (r = i.separatorLocations[","]) == null ? void 0 : r.map( (p) => p + o ); n[","] = [...n[","], ...h ?? []]; const f = (c = i.separatorLocations["-"]) == null ? void 0 : c.map( (p) => p + o ); n["-"] = [...n["-"], ...f ?? []]; } o += i !== void 0 ? i.length : 0; }), n; } function ce(e, t) { const n = ie(e.group, t), o = n.filter((s) => s.guess !== void 0); return n.length > 0 && n.length === o.length; } function at(e, t) { const n = []; return ie(e.group, t).forEach((s) => { n.push(...s.clueIds); }), Array.from(new Set(n)); } function dt(e, t, n) { return e.map((o) => ({ ...o, answered: ce(o, t), selected: o.id === n })).sort( (o, s) => o.direction.localeCompare(s.direction) || o.number - s.number ); } const ft = [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight" ]; function ht(e) { return ft.includes(e); } function pt(e, t) { return t === void 0 ? !1 : e.col === t.col && e.row === t.row; } function mt({ cellMatcher: e, cells: t, cellSize: n, clues: o, cols: s, guessGrid: i, inputRef: r, onCellChange: c, onCellFocus: h, onComplete: f, rawClues: p, rows: d, setGuessGrid: m }) { const w = B(), l = t.find((g) => g.selected), a = o.find((g) => g.selected), I = s * n + s + 1, L = d * n + d + 1, [_, R] = C.useState(i), W = ot(_, 1e3), O = C.useRef(null), [$, D] = C.useState(1), M = V((g) => g.select), j = V((g) => g.setCells), F = Y((g) => g.select), Z = Y((g) => g.answerSome), te = V((g) => g.checkComplete), v = C.useCallback(() => { if (O.current !== null) { const g = O.current.clientWidth, x = O.current.clientHeight, S = g / I, k = x / L, y = Math.min(S, k); D(y); } }, [O.current]); C.useEffect(() => (window.addEventListener("resize", v), v(), function() { window.removeEventListener("resize", v); }), [v]), C.useEffect(() => { m(W); }, [W]); const b = (g, x) => { c !== void 0 && g.guess !== x && c({ pos: g.pos, guess: x, previousGuess: g.guess }); }, N = (g, x) => { h !== void 0 && h({ pos: g, clueId: x }); }, A = (g) => { R(Ge(s, d, g)); }, P = () => { if (a === void 0 || l === void 0) return; if (a.direction === "across" && l.pos.col === a.position.x || a.direction === "down" && l.pos.row === a.position.y) { const x = a.group.indexOf(a.id); if (x > 0) { const S = a.group[x - 1], k = o.find((y) => y.id === S); if (k !== void 0) { const y = { col: k.position.x + (k.direction === "across" ? k.length - 1 : 0), row: k.position.y + (k.direction === "down" ? k.length - 1 : 0) }; F(S), M(y), N(y, S); } } } else { const x = a.direction === "across" ? { col: l.pos.col - 1, row: l.pos.row } : { col: l.pos.col, row: l.pos.row - 1 }; M(x), N(x, a.id); } }, q = () => { if (a === void 0 || l === void 0) return; if (a.direction === "across" && l.pos.col === a.position.x + a.length - 1 || a.direction === "down" && l.pos.row === a.position.y + a.length - 1) { const x = a.group.indexOf(a.id); if (a.group.length - 1 > x) { const S = a.group[x + 1], k = o.find((y) => y.id === S); if (k !== void 0) { const y = { col: k.position.x, row: k.position.y }; F(S), M(y), N(y, S); } } } else { const x = a.direction === "across" ? { col: l.pos.col + 1, row: l.pos.row } : { col: l.pos.col, row: l.pos.row + 1 }; M(x), N(x, a.id); } }, X = (g, x) => { const S = (T, J, Q) => { const ne = T + J; return ne === -1 ? Q - 1 : ne === Q ? 0 : ne; }; let { col: k, row: y } = l == null ? void 0 : l.pos; for (; ; ) { g === 1 || g === -1 ? k = S(k, g, s) : (x === 1 || x === -1) && (y = S(y, x, d)); const T = t.find( // eslint-disable-next-line @typescript-eslint/no-loop-func (J) => J.pos.col === k && J.pos.row === y ); if (T !== void 0) return T; } }, E = (g) => { if (a === void 0 || l === void 0) return; const x = t.find( (k) => k.pos.col === l.pos.col && k.pos.row === l.pos.row ); if (!x) return; if (x.clueIds.length === 2) { const k = g === "ArrowUp" || g === "ArrowDown", y = g === "ArrowLeft" || g === "ArrowRight"; if (k && a.direction === "across" || y && a.direction === "down") { const T = x.clueIds.find( (J) => J !== a.id ); if (T !== void 0) { F(T), N(l.pos, T); return; } } } let S; switch (g) { case "ArrowUp": S = X(0, -1); break; case "ArrowDown": S = X(0, 1); break; case "ArrowLeft": S = X(-1, 0); break; case "ArrowRight": S = X(1, 0); break; default: S = void 0; } if (S !== void 0) if (M(S.pos), S.clueIds.includes(a.id)) N(S.pos, a.id); else { const k = S.clueIds.find( (y) => y.endsWith(a.direction) ) ?? S.clueIds[0]; F(k), N(S.pos, k); } }, H = (g) => { if (!(a === void 0 || l === void 0) && [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Backspace", "Delete", "Tab" ].includes(g.key)) { if (g.preventDefault(), g.stopPropagation(), ht(g.key)) E(g.key); else if (["Backspace", "Delete"].includes(g.key)) { b(l, void 0); const x = { ...l, guess: void 0 }, S = ue({ newCell: x, cells: t }); j(S), l.clueIds.forEach((k) => { const y = o.find((T) => T.id === k); if (y) { const T = ce(y, S); Z(y.group, T); } }), g.key === "Backspace" && P(), A(S); } else if (g.key === "Tab") { const x = o.findIndex((T) => T.selected); let S = 0; g.shiftKey ? S = x > 0 ? x - 1 : o.length - 1 : S = x < o.length - 1 ? x + 1 : 0; const k = o[S], y = { col: k.position.x, row: k.position.y }; F(k.id), M(y), N(y, k.id); } } }, K = (g) => { if (a === void 0 || l === void 0) return; const x = g.target.value.toUpperCase(); if (Ae(x, e)) { b(l, x); const S = { ...l, guess: x }, k = ue({ newCell: S, cells: t }); j(k), l.clueIds.forEach((y) => { const T = o.find((Q) => Q.id === y); ce(T, k) && Z(T.group, !0); }), q(), f !== void 0 && te() === !0 && f(), A(k); } else g.preventDefault(); }, U = l !== void 0 ? Le(n, l.pos) : void 0; return /* @__PURE__ */ G( "div", { className: w("Grid"), "data-testid": "grid", style: { minWidth: I, minHeight: L, width: I, height: L, aspectRatio: `${s} / ${d}` }, children: [ /* @__PURE__ */ G( "svg", { preserveAspectRatio: "xMinYMin", ref: O, viewBox: `0 0 ${I} ${L}`, children: [ /* @__PURE__ */ u( "rect", { className: w("Grid__background"), onMouseDown: (g) => { g.preventDefault(); const x = document.querySelector(".GridInput"); x !== null && x.blur(); }, width: I, height: L, x: "0", y: "0" } ), t.map((g) => { const { clueIds: x, guess: S, num: k, pos: y, checked: T, val: J } = g, Q = pt(y, l == null ? void 0 : l.pos), ne = x.some( (Te) => a == null ? void 0 : a.group.includes(Te) ), He = a !== void 0 ? x.indexOf(a.id) : -1; return /* @__PURE__ */ u( et, { cellSize: n, checked: T, clueIds: x, guess: S, highlighted: ne, inputRef: r, num: k, onCellFocus: h, pos: y, selected: Q, selectedClueIndex: He, val: J }, `${y.col},${y.row}` ); }), /* @__PURE__ */ u(it, { cellSize: n, clues: p }) ] } ), /* @__PURE__ */ u( "div", { className: w("Grid__inputContainer"), style: { width: l !== void 0 ? n * $ : void 0, height: l !== void 0 ? n * $ : void 0, top: (U == null ? void 0 : U.yRect) !== void 0 ? U.yRect * $ : void 0, left: (U == null ? void 0 : U.xRect) !== void 0 ? U.xRect * $ : void 0 }, children: /* @__PURE__ */ u( nt, { onChange: K, onKeyDown: H, ref: r, visible: l !== void 0 } ) } ) ] } ); } function gt({ allowedHtmlTags: e, answered: t, col: n, containerRef: o, id: s, inputRef: i, isHighlighted: r, num: c, onCellFocus: h, row: f, scrollTo: p, text: d }) { const m = B(), w = C.useRef(null), l = V((_) => _.select), a = Y((_) => _.select), I = (_, R) => { h !== void 0 && h({ pos: _, clueId: R }); }, L = C.useCallback(() => { var R; const _ = { col: n, row: f }; a(s), l(_), I(_, s), (R = i == null ? void 0 : i.current) == null || R.focus({ preventScroll: !0 }); }, [i]); return C.useEffect(() => { if (p && w.current !== null && o !== void 0 && o.current !== null) { const _ = w.current.getBoundingClientRect(), R = o.current.getBoundingClientRect(); Ue(_, R) || w.current.scrollIntoView({ behavior: "auto", block: "nearest", inline: "nearest" }); } }, [p]), /* @__PURE__ */ G( "div", { className: m( "Clue", t ? "Clue--answered" : null, r ? "Clue--highlighted" : null ), onClick: L, onKeyDown: (_) => { _.key === "Enter" && L(); }, role: "button", ref: w, tabIndex: 0, children: [ /* @__PURE__ */ u("span", { className: m("Clue__num"), children: c }), /* @__PURE__ */ u( "span", { className: m("Clue__text"), dangerouslySetInnerHTML: { __html: se(re(d), { allowedTags: e }) }, "data-text": se(re(d)) } ) ] } ); } const xe = C.memo(gt); const wt = [ { name: "xs", max: 576 }, { name: "sm", max: 768 }, { name: "md", max: 992 }, { name: "lg", max: 1200 }, { name: "xl", max: 1400 }, { name: "xxl", max: 99999 } ]; function vt() { const [e, t] = C.useState(), [n, o] = C.useState(), s = () => { o(window.innerWidth); }; return C.useEffect(() => { if (window.addEventListener("resize", s), s(), n !== void 0) { const i = wt.filter((r) => n < r.max); i.length > 0 ? t(i[0].name) : t(void 0); } return function() { window.removeEventListener("resize", s); }; }, [n]), e; } function Ct({ allowedHtmlTags: e, entries: t, gridHeight: n, inputRef: o, onCellFocus: s, selectedClueId: i }) { const r = vt(), c = C.useRef(null), h = C.useRef(null), f = C.useRef(null), p = t.filter((l) => l.direction === "across").sort((l, a) => l.number - a.number), d = t.filter((l) => l.direction === "down").sort((l, a) => l.number - a.number), m = (l) => { if (i === void 0) return !1; const a = t.find((I) => I.id === i); return (a == null ? void 0 : a.group.includes(l.id)) ?? !1; }, w = (l) => r !== void 0 && ["md", "lg", "xl", "xxl"].includes(r) && i !== void 0 && l.group.includes(i) && l.id === l.group[0]; return /* @__PURE__ */ G( "div", { className: "Clues", ref: c, style: { maxHeight: n }, children: [ /* @__PURE__ */ G("div", { className: "Clues__list Clues__list--across", ref: h, children: [ /* @__PURE__ */ u("h3", { className: "Clues__listHeader", children: "Across" }), /* @__PURE__ */ u("div", { className: "Clues__listBody", children: p.map((l) => /* @__PURE__ */ u( xe, { allowedHtmlTags: e, answered: l.answered, col: l.position.x, containerRef: r === "md" ? c : h, id: l.id, inputRef: o, isHighlighted: m(l), num: l.humanNumber, onCellFocus: s, row: l.position.y, scrollTo: w(l), text: l.clue }, l.id )) }) ] }), /* @__PURE__ */ G("div", { className: "Clues__list Clues__list--down", ref: f, children: [ /* @__PURE__ */ u("h3", { className: "Clues__listHeader", children: "Down" }), /* @__PURE__ */ u("div", { className: "Clues__listBody", children: d.map((l) => /* @__PURE__ */ u( xe, { allowedHtmlTags: e, answered: l.answered, col: l.position.x, containerRef: r === "md" ? c : f, id: l.id, inputRef: o, isHighlighted: m(l), num: l.humanNumber, onCellFocus: s, row: l.position.y, scrollTo: w(l), text: l.clue }, l.id )) }) ] }) ] } ); } function _e(e) { return e.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } function bt({ allowedHtmlTags: e, className: t, clue: n, onClick: o, splitWords: s = !1 }) { if (!s) return /* @__PURE__ */ u( "span", { dangerouslySetInnerHTML: { __html: se(re(n), { allowedTags: e }) } } ); const r = se(re(n)).split(/\b([\p{L}\p{M}]+)\b/u); return /* @__PURE__ */ u(ee, { children: r.map((c, h) => h % 2 === 1 ? /* @__PURE__ */ u( "span", { className: t, onClick: () => o(_e(c)), onKeyDown: (f) => { f.key === "Enter" && o(_e(c)); }, role: "button", tabIndex: 0, children: c }, `${c}-${h}` ) : /* @__PURE__ */ u("span", { children: c }, `${c}-${h}`)) }); } function xt(e, t) { if (e[","].includes(t)) return "SolutionDisplay__letter--hasSpace"; if (e["-"].includes(t)) return "SolutionDisplay__letter--hasHyphen"; } function _t(e, t) { let n = e; return t.split("").forEach((o) => { n = n.replace(o, ""); }), n; } function Et({ cells: e, letters: t, separators: n, shuffling: o }) { const s = B(), i = e.map((f) => f.guess).join(""), r = t !== void 0 ? _t(t == null ? void 0 : t.toUpperCase(), i) : void 0; let c = t == null ? void 0 : t.toUpperCase(), h = 0; return /* @__PURE__ */ u("div", { className: "SolutionDisplay", children: e.map((f, p) => { const d = f.guess !== void 0 && (c == null ? void 0 : c.includes(f.guess)); return d && (c = c == null ? void 0 : c.replace(f.guess, "")), /* @__PURE__ */ u( "span", { className: s( "SolutionDisplay__letter", f.guess !== void 0 ? "SolutionDisplay__letter--populated" : null, o && f.guess !== void 0 && t !== void 0 && !d ? "SolutionDisplay__letter--missing" : null, xt(n, p + 1) ), children: f.guess ?? (o && r !== void 0 && r[h] !== void 0 ? r[h++] : null) }, `${f.val}-${p}` ); }) }); } const Ee = (e) => Math.round(e * 100) / 100, kt = (e, t, n) => { const o = t * Math.PI / 180 * n; return { left: `${e + Ee(e * Math.sin(o))}%`, top: `${e + Ee(e * Math.cos(o))}%` }; }, St = (e) => ({ left: `${e - 1}%`, top: `${e - 2}%` }), Nt = (e, t = 5) => e.length === 0 ? 0 : e.length < t ? 360 / e.length : 360 / (e.length - 1); function yt({ letters: e, populatedLetters: t }) { const n = B(), o = Nt(e), s = 40; let i = t.toUpperCase(); return /* @__PURE__ */ u("div", { className: n("WordWheel"), children: e.toUpperCase().split("").map((r, c) => { const h = i.includes(r); return h && (i = i.replace(r, "")), /* @__PURE__ */ u( "span", { className: n( "WordWheel__letter", c === 0 && (e.length === 1 || e.length > 4) ? "WordWheel__letter--central" : null, h ? "WordWheel__letter--populated" : null ), style: c === 0 && (e.length === 1 || e.length > 4) ? St(s) : kt(s, o, c), children: r }, `${r}-${c}` ); }) }); } const It = C.forwardRef( ({ ariaLabel: e, children: t, className: n, disabled: o, id: s, onClick: i, onKeyDown: r, variant: c = "filled" }, h) => { const f = B(); return /* @__PURE__ */ u( "button", { "aria-label": e, className: de(f("Button", `Button--${c}`), n), disabled: o, id: s, onClick: i, onKeyDown: r, ref: h, type: "button", children: t } ); } ), z = It; function At({ className: e }) { return /* @__PURE__ */ u( "svg", { className: e, xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 28 28", children: /* @__PURE__ */ u("path", { d: "M21 9.8l-.8-.8-5.2 4.8-5.2-4.8-.8.8 4.8 5.2-4.8 5.2.8.8 5.2-4.8 5.2 4.8.8-.8-4.8-5.2 4.8-5.2" }) } ); } function $t({ allowedHtmlTags: e, clue: t, groupCells: n, groupSeparators: o, onClose: s, style: i }) { const r = B(), c = C.useRef(null), h = C.useRef(null), [f, p] = C.useState(""), [d, m] = C.useState(!1), w = f !== "" || d, l = n.length; C.useEffect(() => { var _; d || (_ = c.current) == null || _.focus({ preventScroll: !0 }); }, [d]); const a = () => { p(""), m(!1); }, I = () => { var _; f !== "" && (p( (R) => R.split("").sort(() => 0.5 - Math.random()).join("") ), m(!0), (_ = h.current) == null || _.focus({ preventScroll: !0 })); }, L = (_) => { var R; p((W) => (W + _).substring(0, l)), (R = c.current) == null || R.focus({ preventScroll: !0 }); }; return C.useEffect(() => { a(); }, [t.id]), /* @__PURE__ */ G( "div", { className: r( "AnagramHelper", d ? "AnagramHelper--shuffling" : null ), style: i, children: [ /* @__PURE__ */ u( z, { ariaLabel: "Close", className: r("AnagramHelper__closeButton"), onClick: s, variant: "outlined", children: /* @__PURE__ */ u(At, { className: r("AnagramHelper__closeButtonIcon") }) } ), /* @__PURE__ */ u("div", { className: r("AnagramHelper__top"), children: d ? /* @__PURE__ */ u( yt, { letters: f, populatedLetters: n.map((_) => _.guess).join("") } ) : /* @__PURE__ */ G(ee, { children: [ /* @__PURE__ */ u( "input", { autoComplete: "off", className: r("AnagramHelper__input"), maxLength: l, onChange: (_) => p(_.target.value), onKeyDown: (_) => { ["Enter", "NumpadEnter"].includes(_.code) ? (_.preventDefault(), I()) : _.code === "Escape" && (f === "" ? s() : a()); }, placeholder: "Enter letters...", ref: c, spellCheck: "false", value: f } ), /* @__PURE__ */ G( "span", { className: r( "AnagramHelper__counter", f === "" ? "AnagramHelper__counter--hidden" : null ), children: [ f.length, "/", l ] } ) ] }) }), /* @__PURE__ */ G("div", { className: r("AnagramHelper__bottom"), children: [ /* @__PURE__ */ G("div", { className: r("AnagramHelper__buttons"), children: [ /* @__PURE__ */ u(z, { disabled: !w, onClick: a, variant: "outlined", children: "Reset" }), /* @__PURE__ */ u( z, { disabled: !w, onClick: I, onKeyDown: (_) => { _.code === "Escape" && a(); }, ref: h, children: "Shuffle" } ) ] }), /* @__PURE__ */ G("p", { className: r("AnagramHelper__clue"), children: [ /* @__PURE__ */ u( "span", { className: r("AnagramHelper__clueNum"), children: `${t.number} ${t.direction}` } ), /* @__PURE__ */ u( bt, { allowedHtmlTags: e, className: r("AnagramHelper__clickableWord"), clue: t.clue, onClick: (_) => L(_), splitWords: !d } ) ] }), /* @__PURE__ */ u( Et, { cells: n, letters: f, separators: o, shuffling: d } ) ] }) ] } ); } const De = typeof window < "u" ? Me : ae; function ke(e) { const t = Ne(() => { throw new Error("Cannot call an event handler while rendering."); }); return De(() => { t.current = e; }, [e]), oe((...n) => { var o; return (o = t.current) == null ? void 0 : o.call(t, ...n); }, [t]); } function Se(e, t, n, o) { const s = Ne(t); De(() => { s.current = t; }, [t]), ae(() => { const i = (n == null ? void 0 : n.current) ?? window; if (!(i && i.addEventListener)) return; const r = (c) => { s.current(c); }; return i.addEventListener(e, r, o), () => { i.removeEventListener(e, r, o); }; }, [e, n, o]); } const fe = typeof window > "u"; function Gt(e, t, n = {}) { const { initializeWithValue: o = !0 } = n, s = oe( (m) => n.serializer ? n.serializer(m) : JSON.stringify(m), [n] ), i = oe( (m) => { if (n.deserializer) return n.deserializer(m); if (m === "undefined") return; const w = t instanceof Function ? t() : t; let l; try { l = JSON.parse(m); } catch (a) { return console.error("Error parsing JSON:", a), w; } return l; }, [n, t] ), r = oe(() => { const m = t instanceof Function ? t() : t; if (fe) return m; try { const w = window.localStorage.getItem(e); return w ? i(w) : m; } catch (w) { return console.warn(`Error reading localStorage key “${e}”:`, w), m; } }, [t, e, i]), [c, h] = ye(() => o ? r() : t instanceof Function ? t() : t), f = ke((m) => { fe && console.warn( `Tried setting localStorage key “${e}” even though environment is not a client` ); try { const w = m instanceof Function ? m(r()) : m; window.localStorage.setItem(e, s(w)), h(w), window.dispatchEvent(new StorageEvent("local-storage", { key: e })); } catch (w) { console.warn(`Error setting localStorage key “${e}”:`, w); } }), p = ke(() => { fe && console.warn( `Tried removing localStorage key “${e}” even though environment is not a client` ); const m = t instanceof Function ? t() : t; window.localStorage.removeItem(e), h(m), window.dispatchEvent(new StorageEvent("local-storage", { key: e })); }); ae(() => { h(r()); }, [e]); const d = oe( (m) => { m.key && m.key !== e || h(r()); }, [e, r] ); return Se("storage", d), Se("local-storage", d), [c, f, p]; } const Rt = 10; function he({ buttonText: e, onCancel: t, onConfirm: n, timeout: o = Rt }) { if (o <= 0) throw new Error("Confirm should have a timeout greater than zero"); const s = B(), [i, r] = C.useState(o); return C.useEffect(() => { const c = setTimeout(() => { i <= 1 ? t() : r((h) => h - 1); }, 1e3); return function() { clearTimeout(c); }; }, [i]), /* @__PURE__ */ G("div", { className: s("Confirm"), children: [ /* @__PURE__ */ G("div", { className: s("Confirm__buttonContainer"), children: [ /* @__PURE__ */ u(z, { onClick: t, variant: "outlined", children: "Cancel" }), /* @__PURE__ */ u(z, { className: s("Confirm__button"), onClick: n, children: e }) ] }), /* @__PURE__ */ G("span", { className: s("Confirm__timeout"), children: [ "This will automatically cancel in ", i ] }) ] }); } function Lt({ className: e }) { return /* @__PURE__ */ u( "svg", { xmlns: "http://www.w3.org/2000/svg", className: e, width: "8", height: "8", viewBox: "0 0 292.362 292.362", children: /* @__PURE__ */ u("path", { d: "M286.935,69.377c-3.614-3.617-7.898-5.424-12.848-5.424H18.274c-4.952,0-9.233,1.807-12.85,5.424 C1.807,72.998,0,77.279,0,82.228c0,4.948,1.807,9.229,5.424,12.847l127.907,127.907c3.621,3.617,7.902,5.428,12.85,5.428 s9.233-1.811,12.847-5.428L286.935,95.074c3.613-3.617,5.427-7.898,5.427-12.847C292.362,77.279,290.548,72.998,286.935,69.377z" }) } ); } function Dt({ className: e, id: t, menu: n, text: o }) { if (n.length < 2) throw new Error("DropdownButton should have at least 2 menu items"); const s = B(), i = C.useRef(null), r = C.useRef(null), c = C.useRef(null), [h, f] = C.useState(!1); C.useEffect(() => { const d = (m) => { const w = i.current !== null && !i.current.contains(m.target); h && w && f(!1); }; return document.addEventListener("click", d), function() { document.removeEventListener("click", d); }; }, [h]); const p = () => { if (c.current !== null && r.current !== null && (c.current.style.marginTop = "", !h)) { const d = c.current.getBoundingClientRect(); if (!je(d)) { const w = d.height + r.current.clientHeight + 10; c.current.style.marginTop = `-${w}px`; } } f((d) => !d); }; return /* @__PURE__ */ G( "div", { className: de( s("DropdownButton", h ? "DropdownButton--expanded" : null), e ), ref: i, children: [ /* @__PURE__ */ G( "button", { "aria-controls": t !== void 0 ? `${t}-listbox` : void 0, "aria-expanded": h ? "true" : "false", "aria-haspopup": "true", className: s("DropdownButton__button"), id: t, onClick: p, ref: r, type: "button", children: [ /* @__PURE__ */ u("span", { children: o }), /* @__PURE__ */ u(Lt, { className: s("DropdownButton__dropdownButtonIcon") }) ] } ), /* @__PURE__ */ u( "ul", { "aria-label": `${o} menu`, className: s("DropdownButton__menu"), id: t !== void 0 ? `${t}-listbox` : void 0, ref: c, role: "listbox", children: n.map((d) => /* @__PURE__ */ u("li", { children: /* @__PURE__ */ u( "button", { className: s("DropdownButton__menuItem"), disabled: d.disabled, onClick: () => { d.onClick(), f(!1); }, type: "button", children: d.text } ) }, d.text)) } ) ] } ); } const pe = C.memo(Dt); function Ht({ cells: e, clues: t, gridCols: n, gridRows: o, onAnagramHelperClick: s, onCellChange: i, onComplete: r, setGuessGrid: c, solutionsAvailable: h }) { const f = B(), p = e.find((v) => v.selected), d = t.find((v) => v.selected), [m, w] = C.useState(!1), [l, a] = C.useState(!1), [I, L] = C.useState(!1), _ = V((v) => v.answerAll), R = V((v) => v.setCells), W = V((v) => v.checkComplete), O = Y((v) => v.answerAll), $ = Y((v) => v.answerSome), D = (v) => { c(Ge(n, o, v)); }, M = (v, b) => { at(v, b).forEach((A) => { const P = t.find((q) => q.id === A); if (P) { const q = ce(P, b); $(P.group, q); } }); }, j = (v, b) => { i !== void 0 && v.guess !== b && i({ pos: v.pos, guess: b, previousGuess: v.guess }); }, F = [ { disabled: p === void 0, onClick: () => { if (p === void 0) return; const v = ue({ cells: e, newCell: { ...p, checked: !0 }, when: (b) => b.guess !== void 0 && b.guess !== "" }); R(v); }, text: "Check letter" }, { disabled: d === void 0, onClick: () => { if (d !== void 0) { const v = ie(d.group, e), b = e.map((N) => v.some( (P) => P.pos.col === N.pos.col && P.pos.row === N.pos.row ) && N.guess !== void 0 && N.guess !== "" ? { ...N, checked: !0 } : N); R(b); } }, text: "Check word" }, { onClick: () => w(!0), text: "Check grid" } ], Z = [ { disabled: p === void 0, onClick: () => { if (p === void 0 || p.guess === p.val) return; j(p, p.val); const v = ue({ newCell: { ...p, guess: p.val, checked: !1 }, cells: e }); R(v), r !== void 0 && W() === !0 && r(), p.clueIds.forEach((b) => { const N = t.find((P) => P.id === b); ce(N, v) && $(N.group, !0); }), D(v); }, text: "Reveal letter" }, { disabled: d === void 0, onClick: () => { if (d === void 0) return; i !== void 0 && ie(d.group, e).forEach((N) => { N.val !== N.guess && j(N, N.val); }); const v = e.map((b) => d.group.filter( (A) => b.clueIds.includes(A) ).length > 0 ? { ...b, guess: b.val, checked: !1 } : b); R(v), r !== void 0 && W() === !0 && r(), M(d, v), D(v); }, text: "Reveal word" }, { onClick: () => a(!0), text: "Reveal grid" } ], te = [ { disabled: d === void 0, onClick: () => { if (d !== void 0) { const v = e.map((b) => { const N = d.group.filter( (A) => b.clueIds.includes(A) ); if (N.length > 0) { if (b.clueIds.length === 1) return j(b, void 0), { ...b, guess: void 0 }; const P = N[0].includes("across"); if (lt(e, b, P)) return j(b, void 0), { ...b, guess: void 0 }; } return b; }); R(v), $(d.group, !1), D(v); } }, text: "Clear word" }, { onClick: () => L(!0), text: "Clear grid" } ]; return m ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( he, { buttonText: "Confirm check grid", onCancel: () => w(!1), onConfirm: () => { const v = e.map( (b) => b.guess !== void 0 && b.guess !== "" ? { ...b, checked: !0 } : b ); R(v), w(!1); } } ) }) : l ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( he, { buttonText: "Confirm reveal grid", onCancel: () => a(!1), onConfirm: () => { i !== void 0 && e.forEach((b) => { b.val !== b.guess && j(b, b.val); }), _(!0), O(!0), a(!1), r !== void 0 && W() === !0 && r(); const v = e.map((b) => ({ ...b, guess: b.val, checked: !1 })); D(v); } } ) }) : I ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( he, { buttonText: "Confirm clear grid", onCancel: () => L(!1), onConfirm: () => { i !== void 0 && e.forEach((v) => { v.guess !== void 0 && j(v, void 0); }), _(!1), O(!1), L(!1), D([]); } } ) }) : /* @__PURE__ */ G("div", { className: f("Controls"), children: [ h ? /* @__PURE__ */ G(ee, { children: [ /* @__PURE__ */ u(pe, { id: "check-control", menu: F, text: "Check" }), /* @__PURE__ */ u(pe, { id: "reveal-control", menu: Z, text: "Reveal" }) ] }) : null, /* @__PURE__ */ u(pe, { id: "clear-control", menu: te, text: "Clear" }), /* @__PURE__ */ u("div", { className: f("Controls__buttonContainer"), children: /* @__PURE__ */ u( z, { ariaLabel: "Anagram helper", disabled: d === void 0, id: "anagram-control", onClick: s, children: /* @__PURE__ */ u("span", {}) } ) }) ] }); } function Tt() { const [e, t] = ye( () => typeof window < "u" ? window.location.hash : "" ); return ae((