UNPKG

mycrossword

Version:
1,753 lines 55.6 kB
import { jsx as u, jsxs as $, Fragment as te } from "react/jsx-runtime"; import * as x from "react"; import he, { useLayoutEffect as De, useEffect as le, useRef as Se, useCallback as ne, useState as ye } from "react"; function He(e) { return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e; } var ke = { 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 o() { for (var r = "", i = 0; i < arguments.length; i++) { var c = arguments[i]; c && (r = n(r, s(c))); } return r; } function s(r) { if (typeof r == "string" || typeof r == "number") return r; if (typeof r != "object") return ""; if (Array.isArray(r)) return o.apply(null, r); if (r.toString !== Object.prototype.toString && !r.toString.toString().includes("[native code]")) return r.toString(); var i = ""; for (var c in r) t.call(r, c) && r[c] && (i = n(i, c)); return i; } function n(r, i) { return i ? r ? r + " " + i : r + i : r; } e.exports ? (o.default = o, e.exports = o) : window.classNames = o; })(); })(ke); var Te = ke.exports; const ue = /* @__PURE__ */ He(Te); function O(e) { return (...t) => ue(t); } const Be = ["b", "strong", "i", "em", "sub", "sup"], Me = /[A-Z]/; function Ae(e, t) { return e.length !== 1 ? !1 : e.match(t); } function We(e) { return e.top >= 0 && e.left >= 0 && e.right <= (window.innerWidth || document.documentElement.clientWidth) && e.bottom <= (window.innerHeight || document.documentElement.clientHeight); } function Pe(e, t) { return e.top >= t.top && e.left >= t.left && e.right <= t.right && e.bottom <= t.bottom; } function Ie(e, t, o = "") { return { value: new Array(e).fill(o).map(() => new Array(t).fill(o)) }; } function $e(e, t, o) { const s = Ie(e, t); return o.forEach(({ guess: n, pos: r }) => { s.value[r.col][r.row] = n !== void 0 ? n : ""; }), s; } function Oe(e, t, o, s) { if (e.value.reduce((r, i) => r + i.length, 0) !== t * o) return !1; for (let r = 0; r < t; r += 1) for (let i = 0; i < o; i += 1) { const c = e.value[r][i]; if (c !== "" && !Ae(c, s)) return !1; } return !0; } const me = (e) => { let t; const o = /* @__PURE__ */ new Set(), s = (f, h) => { const d = typeof f == "function" ? f(t) : f; if (!Object.is(d, t)) { const v = t; t = h ?? (typeof d != "object" || d === null) ? d : Object.assign({}, t, d), o.forEach((C) => C(t, v)); } }, n = () => t, c = { setState: s, getState: n, getInitialState: () => p, subscribe: (f) => (o.add(f), () => o.delete(f)) }, p = t = e(s, n, c); return c; }, je = (e) => e ? me(e) : me, Ue = (e) => e; function Ve(e, t = Ue) { const o = he.useSyncExternalStore( e.subscribe, () => t(e.getState()), () => t(e.getInitialState()) ); return he.useDebugValue(o), o; } const ge = (e) => { const t = je(e), o = (s) => Ve(t, s); return Object.assign(o, t), o; }, Re = (e) => e ? ge(e) : ge, Y = Re((e) => ({ clues: [], setClues: (t) => { e(() => ({ clues: t })); }, select: (t) => { e((o) => (typeof window < "u" && window.history.replaceState(null, "", `#${t}`), { clues: o.clues.map((s) => ({ ...s, selected: s.id === t })) })); }, answerAll: (t) => { e((o) => ({ clues: o.clues.map((s) => ({ ...s, answered: t })) })); }, answerSome: (t, o) => { e((s) => ({ clues: s.clues.map((n) => t.includes(n.id) ? { ...n, answered: o } : n) })); } })), V = Re((e, t) => ({ cells: [], complete: !1, checkComplete: () => { if (t().complete) return null; const o = t().cells.every((s) => s.val === s.guess); return e({ complete: o }), o; }, resetComplete: () => { e({ complete: !1 }); }, setCells: (o) => { e(() => ({ cells: o })); }, select: (o) => { e((s) => ({ cells: s.cells.map((n) => ({ ...n, selected: n.pos.col === o.col && n.pos.row === o.row })) })); }, answerAll: (o) => { e((s) => ({ cells: s.cells.map((n) => ({ ...n, guess: o ? n.val : void 0 })) })); } })); function Fe({ message: e, style: t }) { const o = O(); return /* @__PURE__ */ u("div", { className: o("GridError"), style: t, children: /* @__PURE__ */ $("div", { role: "alert", children: [ /* @__PURE__ */ u("h1", { className: o("GridError__title"), children: "Something went wrong" }), /* @__PURE__ */ u("p", { className: o("GridError__subTitle"), children: e }) ] }) }); } function Ke({ 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 qe({ 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: o = [] } = t; if (!/<|>/.test(e)) return e; let s = "", n = e; const r = o.length > 0 ? `\\b(${o.join("|")})\\b` : "(?!)", i = new RegExp(`</?(?!${r})\\w+[^>]*>`, "gi"), c = /<(\w+)[^>]*>/gi; for (; s !== n; ) s = n, n = n.replace(i, ""), n = n.replace(c, "<$1>"); return n; } const Je = { "&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, o) => { const s = Je[t.toLowerCase()]; if (s !== void 0) return s; if (o.startsWith("#")) { let n; if (o.startsWith("#x") || o.startsWith("#X") ? n = parseInt(o.slice(2), 16) : n = parseInt(o.slice(1), 10), !isNaN(n)) try { return String.fromCodePoint(n); } catch { return t; } } return t; }); } function Ye({ allowedTags: e, num: t, onMoveNext: o, onMovePrev: s, show: n, text: r }) { const i = O(); return /* @__PURE__ */ u("div", { className: i("StickyClue", `StickyClue--${n}`), children: r !== void 0 && t !== void 0 ? /* @__PURE__ */ $(te, { children: [ /* @__PURE__ */ u( "button", { "aria-label": "Previous clue", className: i("StickyClue__button"), onClick: s, type: "button", children: /* @__PURE__ */ u(Ke, {}) } ), /* @__PURE__ */ u("div", { className: i("StickyClue__inner"), children: /* @__PURE__ */ $("span", { className: i("StickyClue__text"), children: [ /* @__PURE__ */ u("span", { className: i("StickyClue__num"), children: t }), /* @__PURE__ */ u( "span", { dangerouslySetInnerHTML: { __html: se(re(r), { allowedTags: e }) } } ) ] }) }), /* @__PURE__ */ u( "button", { "aria-label": "Next clue", className: i("StickyClue__button"), onClick: o, type: "button", children: /* @__PURE__ */ u(qe, {}) } ) ] }) : null }); } const Ge = (e, t) => { const o = 1 + (e + 1) * t.col, s = 1 + (e + 1) * t.row, n = o + 1, r = s + Math.ceil(e * 0.29), i = o + Math.ceil(e * 0.5), c = s + Math.ceil(e * 0.675); return { xRect: o, yRect: s, xNum: n, yNum: r, xText: i, yText: c }; }; function Xe({ cellSize: e, clueIds: t, guess: o, inputRef: s, isHighlighted: n, isSelected: r, num: i, onCellFocus: c, pos: p, selectedClueIndex: f }) { if (t.length !== 1 && t.length !== 2) throw new Error( "Crossword data error: cell does not have 1 or 2 directions" ); const h = O(), d = Y((L) => L.select), v = V((L) => L.select), { xRect: C, yRect: l, xNum: a, yNum: G, xText: H, yText: _ } = Ge( e, p ), R = (L, k) => { c !== void 0 && c({ pos: L, clueId: k }); }, M = () => { var B; let L = f === -1 ? 0 : f; t.length === 2 && r && (L = f === 0 ? 1 : 0); const k = t[L]; d(k), r || v(p), (!r || t.length === 2) && R(p, k), (B = s == null ? void 0 : s.current) == null || B.focus({ preventScroll: !0 }); }; return /* @__PURE__ */ $( "g", { className: h( "GridCell", n ? "GridCell--highlighted" : null, r ? "GridCell--selected" : null ), onClick: M, children: [ /* @__PURE__ */ u( "rect", { className: h("GridCell__rect"), x: C, y: l, width: e, height: e } ), i ? /* @__PURE__ */ u( "text", { className: h("GridCell__num"), x: a, y: G, style: { fontSize: Math.ceil(e * 0.32) }, children: i } ) : null, /* @__PURE__ */ u( "text", { className: h("GridCell__text"), textAnchor: "middle", x: H, y: _, style: { fontSize: Math.ceil(e * 0.55) }, children: o } ) ] } ); } const Ze = x.memo(Xe); const Qe = x.forwardRef( ({ onChange: e, onKeyDown: t, visible: o }, s) => { const n = O(); return /* @__PURE__ */ u( "input", { autoComplete: "off", autoCorrect: "off", autoFocus: !1, className: n( "GridInput", o ? null : "GridInput--inclusivelyHidden" ), maxLength: 1, onChange: e, onKeyDown: t, ref: s, spellCheck: "false", tabIndex: -1, type: "text", value: "" } ); } ), ze = Qe; function et(e, t) { const [o, s] = x.useState(e); return x.useEffect(() => { const n = setTimeout(() => { s(e); }, t); return () => { clearTimeout(n); }; }, [e, t]), o; } function we(e, t) { return e * (t + 1) + 1; } function tt({ char: e, col: t, row: o, direction: s, cellSize: n }) { const r = we(o, n), i = we(t, n), c = s === "across"; if (e === ",") { const p = c ? 1 : n, f = c ? n : 1, h = c ? i - 2 : i, d = c ? r : r - 2; return /* @__PURE__ */ u("rect", { width: p, height: f, x: h, y: d }); } if (e === "-") { const p = c ? n * 0.25 : 1, f = c ? 1 : n * 0.25, h = c ? i - 0.5 - p * 0.5 : i + n * 0.5 + p * 0.5, d = c ? r + n * 0.5 + f * 0.5 : r - 0.5 - f * 0.5; return /* @__PURE__ */ u("rect", { width: p, height: f, x: h, y: d }); } return /* @__PURE__ */ u(te, {}); } function ve(e, t, o, s) { if (t <= 0 || t >= o.length) return null; const n = o.position.x + (o.direction === "across" ? t : 0), r = o.position.y + (o.direction === "across" ? 0 : t); return /* @__PURE__ */ u( tt, { cellSize: s, char: e, direction: o.direction, col: n, row: r }, [n, r, o.direction].join(",") ); } function ot({ clues: e, cellSize: t }) { return /* @__PURE__ */ u("svg", { className: "GridSeparators", children: e.filter((o) => Object.keys(o.separatorLocations).length > 0).map((o) => { const s = [], n = o.separatorLocations[","], r = o.separatorLocations["-"]; return n !== void 0 && s.push( n.map((i) => ve(",", i, o, t)) ), r !== void 0 && s.push( r.map((i) => ve("-", i, o, t)) ), s; }) }); } const nt = x.memo(ot); function ce(e, t) { return t.map((o) => o.pos.col === e.pos.col && o.pos.row === e.pos.row ? e : o); } function st({ cols: e, rows: t, entries: o, guessGrid: s, allowMissingSolutions: n = !1 }) { const r = [], i = o.map((c) => c.id); return o.forEach((c) => { var p, f; for (let h = 0; h < c.length; h += 1) { const d = c.direction === "across", v = d ? c.position.x + h : c.position.x, C = d ? c.position.y : c.position.y + h; if (v < 0 || v >= e || C < 0 || C >= t) throw new Error("Crossword data error: out of bounds"); if (!n && 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) => i.includes(a))) throw new Error("Crossword data error: group clue id not found"); const l = r.find( ({ pos: a }) => a.col === v && a.row === C ); if (l === void 0) { const a = s == null ? void 0 : s.value[v][C], G = { clueIds: [c.id], guess: a !== "" ? a : void 0, num: h === 0 ? c.number : void 0, pos: { col: v, row: C }, selected: !1, val: (p = c.solution) == null ? void 0 : p[h] }; r.push(G); } else { if (d && l.clueIds.some((G) => G.endsWith("across"))) throw new Error("Crossword data error: overlapping across solutions"); if (!d && l.clueIds.some((G) => G.endsWith("down"))) throw new Error("Crossword data error: overlapping down solutions"); const a = (f = c.solution) == null ? void 0 : f[h]; if (!n && l.val !== a) throw new Error("Crossword data error: solution character clash"); l.num = h === 0 ? c.number : l.num, l.clueIds = [...l.clueIds, c.id], n && a !== void 0 && (l.val = a); } } }), r; } function Ce(e, t) { return e.find( (o) => o.pos.col === t.col && o.pos.row === t.row ); } function rt(e, t, o) { const s = Ce(e, { col: t.pos.col - (o ? 0 : 1), row: t.pos.row - (o ? 1 : 0) }), n = Ce(e, { col: t.pos.col + (o ? 0 : 1), row: t.pos.row + (o ? 1 : 0) }); return (s == null ? void 0 : s.guess) === void 0 && (n == null ? void 0 : n.guess) === void 0; } function ie(e, t) { const o = []; return e.forEach((s) => { const n = t.filter((r) => r.clueIds.includes(s)).sort( (r, i) => r.pos.col - i.pos.col || r.pos.row - i.pos.row ); o.push(...n); }), o; } function it(e, t) { const o = { ",": [], "-": [] }; let s = 0; return e.forEach((n) => { var i, c; const r = t.find((p) => p.id === n); if (r !== void 0) { const p = (i = r.separatorLocations[","]) == null ? void 0 : i.map( (h) => h + s ); o[","] = [...o[","], ...p ?? []]; const f = (c = r.separatorLocations["-"]) == null ? void 0 : c.map( (h) => h + s ); o["-"] = [...o["-"], ...f ?? []]; } s += r !== void 0 ? r.length : 0; }), o; } function z(e, t) { const o = ie(e.group, t), s = o.filter((n) => n.guess !== void 0); return o.length > 0 && o.length === s.length; } function ct(e, t) { const o = []; return ie(e.group, t).forEach((n) => { o.push(...n.clueIds); }), Array.from(new Set(o)); } function lt(e, t, o) { return e.map((s) => ({ ...s, answered: z(s, t), selected: s.id === o })).sort( (s, n) => s.direction.localeCompare(n.direction) || s.number - n.number ); } const ut = [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight" ]; function at(e) { return ut.includes(e); } function dt(e, t) { return t === void 0 ? !1 : e.col === t.col && e.row === t.row; } function ft({ cellMatcher: e, cells: t, cellSize: o, clues: s, cols: n, guessGrid: r, inputRef: i, onCellChange: c, onCellFocus: p, onComplete: f, rawClues: h, rows: d, setGuessGrid: v }) { const C = O(), l = t.find((m) => m.selected), a = s.find((m) => m.selected), G = n * o + n + 1, H = d * o + d + 1, [_, R] = x.useState(r), M = et(_, 1e3), L = x.useRef(null), [k, B] = x.useState(1), j = V((m) => m.select), W = V((m) => m.setCells), F = Y((m) => m.select), Z = Y((m) => m.answerSome), oe = V((m) => m.checkComplete), w = x.useCallback(() => { if (L.current !== null) { const m = L.current.clientWidth, b = L.current.clientHeight, y = m / G, E = b / H, I = Math.min(y, E); B(I); } }, [L.current]); x.useEffect(() => (window.addEventListener("resize", w), w(), function() { window.removeEventListener("resize", w); }), [w]), x.useEffect(() => { v(M); }, [M]); const g = (m, b) => { c !== void 0 && m.guess !== b && c({ pos: m.pos, guess: b, previousGuess: m.guess }); }, S = (m, b) => { p !== void 0 && p({ pos: m, clueId: b }); }, A = (m) => { R($e(n, d, m)); }, U = () => { 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 b = a.group.indexOf(a.id); if (b > 0) { const y = a.group[b - 1], E = s.find((I) => I.id === y); if (E !== void 0) { const I = { col: E.position.x + (E.direction === "across" ? E.length - 1 : 0), row: E.position.y + (E.direction === "down" ? E.length - 1 : 0) }; F(y), j(I), S(I, y); } } } else { const b = a.direction === "across" ? { col: l.pos.col - 1, row: l.pos.row } : { col: l.pos.col, row: l.pos.row - 1 }; j(b), S(b, 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 b = a.group.indexOf(a.id); if (a.group.length - 1 > b) { const y = a.group[b + 1], E = s.find((I) => I.id === y); if (E !== void 0) { const I = { col: E.position.x, row: E.position.y }; F(y), j(I), S(I, y); } } } else { const b = a.direction === "across" ? { col: l.pos.col + 1, row: l.pos.row } : { col: l.pos.col, row: l.pos.row + 1 }; j(b), S(b, a.id); } }, X = (m, b) => { const y = (T, J, Q) => { const ae = T + J; return ae === -1 ? Q - 1 : ae === Q ? 0 : ae; }; let { col: E, row: I } = l == null ? void 0 : l.pos; for (; ; ) { m === 1 || m === -1 ? E = y(E, m, n) : (b === 1 || b === -1) && (I = y(I, b, d)); const T = t.find( // eslint-disable-next-line @typescript-eslint/no-loop-func (J) => J.pos.col === E && J.pos.row === I ); if (T !== void 0) return T; } }, N = (m) => { if (a === void 0 || l === void 0) return; const b = t.find( (E) => E.pos.col === l.pos.col && E.pos.row === l.pos.row ); if (!b) return; if (b.clueIds.length === 2) { const E = m === "ArrowUp" || m === "ArrowDown", I = m === "ArrowLeft" || m === "ArrowRight"; if (E && a.direction === "across" || I && a.direction === "down") { const T = b.clueIds.find( (J) => J !== a.id ); if (T !== void 0) { F(T), S(l.pos, T); return; } } } let y; switch (m) { case "ArrowUp": y = X(0, -1); break; case "ArrowDown": y = X(0, 1); break; case "ArrowLeft": y = X(-1, 0); break; case "ArrowRight": y = X(1, 0); break; default: y = void 0; } if (y !== void 0) if (j(y.pos), y.clueIds.includes(a.id)) S(y.pos, a.id); else { const E = y.clueIds.find( (I) => I.endsWith(a.direction) ) ?? y.clueIds[0]; F(E), S(y.pos, E); } }, D = (m) => { if (!(a === void 0 || l === void 0) && [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Backspace", "Delete", "Tab" ].includes(m.key)) { if (m.preventDefault(), m.stopPropagation(), at(m.key)) N(m.key); else if (["Backspace", "Delete"].includes(m.key)) { g(l, void 0); const b = { ...l, guess: void 0 }, y = ce(b, t); W(y), l.clueIds.forEach((E) => { const I = s.find((T) => T.id === E); if (I) { const T = z(I, y); Z(I.group, T); } }), m.key === "Backspace" && U(), A(y); } else if (m.key === "Tab") { const b = s.findIndex((T) => T.selected); let y = 0; m.shiftKey ? y = b > 0 ? b - 1 : s.length - 1 : y = b < s.length - 1 ? b + 1 : 0; const E = s[y], I = { col: E.position.x, row: E.position.y }; F(E.id), j(I), S(I, E.id); } } }, K = (m) => { if (a === void 0 || l === void 0) return; const b = m.target.value.toUpperCase(); if (Ae(b, e)) { g(l, b); const y = { ...l, guess: b }, E = ce(y, t); W(E), l.clueIds.forEach((I) => { const T = s.find((Q) => Q.id === I); z(T, E) && Z(T.group, !0); }), q(), f !== void 0 && oe() === !0 && f(), A(E); } else m.preventDefault(); }, P = l !== void 0 ? Ge(o, l.pos) : void 0; return /* @__PURE__ */ $( "div", { className: C("Grid"), "data-testid": "grid", style: { minWidth: G, minHeight: H, width: G, height: H, aspectRatio: `${n} / ${d}` }, children: [ /* @__PURE__ */ $( "svg", { preserveAspectRatio: "xMinYMin", ref: L, viewBox: `0 0 ${G} ${H}`, children: [ /* @__PURE__ */ u( "rect", { className: C("Grid__background"), onMouseDown: (m) => { m.preventDefault(); const b = document.querySelector(".GridInput"); b !== null && b.blur(); }, width: G, height: H, x: "0", y: "0" } ), t.map(({ clueIds: m, guess: b, num: y, pos: E }) => { const I = dt(E, l == null ? void 0 : l.pos), T = m.some( (Q) => a == null ? void 0 : a.group.includes(Q) ), J = a !== void 0 ? m.indexOf(a.id) : -1; return /* @__PURE__ */ u( Ze, { cellSize: o, clueIds: m, guess: b, inputRef: i, isHighlighted: T, isSelected: I, num: y, onCellFocus: p, pos: E, selectedClueIndex: J }, `${E.col},${E.row}` ); }), /* @__PURE__ */ u(nt, { cellSize: o, clues: h }) ] } ), /* @__PURE__ */ u( "div", { className: C("Grid__inputContainer"), style: { width: l !== void 0 ? o * k : void 0, height: l !== void 0 ? o * k : void 0, top: (P == null ? void 0 : P.yRect) !== void 0 ? P.yRect * k : void 0, left: (P == null ? void 0 : P.xRect) !== void 0 ? P.xRect * k : void 0 }, children: /* @__PURE__ */ u( ze, { onChange: K, onKeyDown: D, ref: i, visible: l !== void 0 } ) } ) ] } ); } function pt({ allowedHtmlTags: e, answered: t, col: o, containerRef: s, id: n, inputRef: r, isHighlighted: i, num: c, onCellFocus: p, row: f, scrollTo: h, text: d }) { const v = O(), C = x.useRef(null), l = V((_) => _.select), a = Y((_) => _.select), G = (_, R) => { p !== void 0 && p({ pos: _, clueId: R }); }, H = x.useCallback(() => { var R; const _ = { col: o, row: f }; a(n), l(_), G(_, n), (R = r == null ? void 0 : r.current) == null || R.focus({ preventScroll: !0 }); }, [r]); return x.useEffect(() => { if (h && C.current !== null && s !== void 0 && s.current !== null) { const _ = C.current.getBoundingClientRect(), R = s.current.getBoundingClientRect(); Pe(_, R) || C.current.scrollIntoView({ behavior: "auto", block: "nearest", inline: "nearest" }); } }, [h]), /* @__PURE__ */ $( "div", { className: v( "Clue", t ? "Clue--answered" : null, i ? "Clue--highlighted" : null ), onClick: H, onKeyDown: (_) => { _.key === "Enter" && H(); }, role: "button", ref: C, tabIndex: 0, children: [ /* @__PURE__ */ u("span", { className: v("Clue__num"), children: c }), /* @__PURE__ */ u( "span", { className: v("Clue__text"), dangerouslySetInnerHTML: { __html: se(re(d), { allowedTags: e }) }, "data-text": se(re(d)) } ) ] } ); } const xe = x.memo(pt); const ht = [ { 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 mt() { const [e, t] = x.useState(), [o, s] = x.useState(), n = () => { s(window.innerWidth); }; return x.useEffect(() => { if (window.addEventListener("resize", n), n(), o !== void 0) { const r = ht.filter((i) => o < i.max); r.length > 0 ? t(r[0].name) : t(void 0); } return function() { window.removeEventListener("resize", n); }; }, [o]), e; } function gt({ allowedHtmlTags: e, entries: t, gridHeight: o, inputRef: s, onCellFocus: n, selectedClueId: r }) { const i = mt(), c = x.useRef(null), p = x.useRef(null), f = x.useRef(null), h = 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), v = (l) => { if (r === void 0) return !1; const a = t.find((G) => G.id === r); return (a == null ? void 0 : a.group.includes(l.id)) ?? !1; }, C = (l) => i !== void 0 && ["md", "lg", "xl", "xxl"].includes(i) && r !== void 0 && l.group.includes(r) && l.id === l.group[0]; return /* @__PURE__ */ $( "div", { className: "Clues", ref: c, style: { maxHeight: o }, children: [ /* @__PURE__ */ $("div", { className: "Clues__list Clues__list--across", ref: p, children: [ /* @__PURE__ */ u("h3", { className: "Clues__listHeader", children: "Across" }), /* @__PURE__ */ u("div", { className: "Clues__listBody", children: h.map((l) => /* @__PURE__ */ u( xe, { allowedHtmlTags: e, answered: l.answered, col: l.position.x, containerRef: i === "md" ? c : p, id: l.id, inputRef: s, isHighlighted: v(l), num: l.humanNumber, onCellFocus: n, row: l.position.y, scrollTo: C(l), text: l.clue }, l.id )) }) ] }), /* @__PURE__ */ $("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: i === "md" ? c : f, id: l.id, inputRef: s, isHighlighted: v(l), num: l.humanNumber, onCellFocus: n, row: l.position.y, scrollTo: C(l), text: l.clue }, l.id )) }) ] }) ] } ); } function be(e) { return e.normalize("NFD").replace(/[\u0300-\u036f]/g, ""); } function wt({ allowedHtmlTags: e, className: t, clue: o, onClick: s, splitWords: n = !1 }) { if (!n) return /* @__PURE__ */ u( "span", { dangerouslySetInnerHTML: { __html: se(re(o), { allowedTags: e }) } } ); const i = se(re(o)).split(/\b([\p{L}\p{M}]+)\b/u); return /* @__PURE__ */ u(te, { children: i.map((c, p) => p % 2 === 1 ? /* @__PURE__ */ u( "span", { className: t, onClick: () => s(be(c)), onKeyDown: (f) => { f.key === "Enter" && s(be(c)); }, role: "button", tabIndex: 0, children: c }, `${c}-${p}` ) : /* @__PURE__ */ u("span", { children: c }, `${c}-${p}`)) }); } function vt(e, t) { if (e[","].includes(t)) return "SolutionDisplay__letter--hasSpace"; if (e["-"].includes(t)) return "SolutionDisplay__letter--hasHyphen"; } function Ct(e, t) { let o = e; return t.split("").forEach((s) => { o = o.replace(s, ""); }), o; } function xt({ cells: e, letters: t, separators: o, shuffling: s }) { const n = O(), r = e.map((f) => f.guess).join(""), i = t !== void 0 ? Ct(t == null ? void 0 : t.toUpperCase(), r) : void 0; let c = t == null ? void 0 : t.toUpperCase(), p = 0; return /* @__PURE__ */ u("div", { className: "SolutionDisplay", children: e.map((f, h) => { 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: n( "SolutionDisplay__letter", f.guess !== void 0 ? "SolutionDisplay__letter--populated" : null, s && f.guess !== void 0 && t !== void 0 && !d ? "SolutionDisplay__letter--missing" : null, vt(o, h + 1) ), children: f.guess ?? (s && i !== void 0 && i[p] !== void 0 ? i[p++] : null) }, `${f.val}-${h}` ); }) }); } const _e = (e) => Math.round(e * 100) / 100, bt = (e, t, o) => { const s = t * Math.PI / 180 * o; return { left: `${e + _e(e * Math.sin(s))}%`, top: `${e + _e(e * Math.cos(s))}%` }; }, _t = (e) => ({ left: `${e - 1}%`, top: `${e - 2}%` }), Et = (e, t = 5) => e.length === 0 ? 0 : e.length < t ? 360 / e.length : 360 / (e.length - 1); function Nt({ letters: e, populatedLetters: t }) { const o = O(), s = Et(e), n = 40; let r = t.toUpperCase(); return /* @__PURE__ */ u("div", { className: o("WordWheel"), children: e.toUpperCase().split("").map((i, c) => { const p = r.includes(i); return p && (r = r.replace(i, "")), /* @__PURE__ */ u( "span", { className: o( "WordWheel__letter", c === 0 && (e.length === 1 || e.length > 4) ? "WordWheel__letter--central" : null, p ? "WordWheel__letter--populated" : null ), style: c === 0 && (e.length === 1 || e.length > 4) ? _t(n) : bt(n, s, c), children: i }, `${i}-${c}` ); }) }); } const St = x.forwardRef( ({ ariaLabel: e, children: t, className: o, disabled: s, id: n, onClick: r, onKeyDown: i, variant: c = "filled" }, p) => { const f = O(); return /* @__PURE__ */ u( "button", { "aria-label": e, className: ue(f("Button", `Button--${c}`), o), disabled: s, id: n, onClick: r, onKeyDown: i, ref: p, type: "button", children: t } ); } ), ee = St; function yt({ 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 kt({ allowedHtmlTags: e, clue: t, groupCells: o, groupSeparators: s, onClose: n, style: r }) { const i = O(), c = x.useRef(null), p = x.useRef(null), [f, h] = x.useState(""), [d, v] = x.useState(!1), C = f !== "" || d, l = o.length; x.useEffect(() => { var _; d || (_ = c.current) == null || _.focus({ preventScroll: !0 }); }, [d]); const a = () => { h(""), v(!1); }, G = () => { var _; f !== "" && (h( (R) => R.split("").sort(() => 0.5 - Math.random()).join("") ), v(!0), (_ = p.current) == null || _.focus({ preventScroll: !0 })); }, H = (_) => { var R; h((M) => (M + _).substring(0, l)), (R = c.current) == null || R.focus({ preventScroll: !0 }); }; return x.useEffect(() => { a(); }, [t.id]), /* @__PURE__ */ $( "div", { className: i( "AnagramHelper", d ? "AnagramHelper--shuffling" : null ), style: r, children: [ /* @__PURE__ */ u( ee, { ariaLabel: "Close", className: i("AnagramHelper__closeButton"), onClick: n, variant: "outlined", children: /* @__PURE__ */ u(yt, { className: i("AnagramHelper__closeButtonIcon") }) } ), /* @__PURE__ */ u("div", { className: i("AnagramHelper__top"), children: d ? /* @__PURE__ */ u( Nt, { letters: f, populatedLetters: o.map((_) => _.guess).join("") } ) : /* @__PURE__ */ $(te, { children: [ /* @__PURE__ */ u( "input", { autoComplete: "off", className: i("AnagramHelper__input"), maxLength: l, onChange: (_) => h(_.target.value), onKeyDown: (_) => { ["Enter", "NumpadEnter"].includes(_.code) ? (_.preventDefault(), G()) : _.code === "Escape" && (f === "" ? n() : a()); }, placeholder: "Enter letters...", ref: c, spellCheck: "false", value: f } ), /* @__PURE__ */ $( "span", { className: i( "AnagramHelper__counter", f === "" ? "AnagramHelper__counter--hidden" : null ), children: [ f.length, "/", l ] } ) ] }) }), /* @__PURE__ */ $("div", { className: i("AnagramHelper__bottom"), children: [ /* @__PURE__ */ $("div", { className: i("AnagramHelper__buttons"), children: [ /* @__PURE__ */ u(ee, { disabled: !C, onClick: a, variant: "outlined", children: "Reset" }), /* @__PURE__ */ u( ee, { disabled: !C, onClick: G, onKeyDown: (_) => { _.code === "Escape" && a(); }, ref: p, children: "Shuffle" } ) ] }), /* @__PURE__ */ $("p", { className: i("AnagramHelper__clue"), children: [ /* @__PURE__ */ u( "span", { className: i("AnagramHelper__clueNum"), children: `${t.number} ${t.direction}` } ), /* @__PURE__ */ u( wt, { allowedHtmlTags: e, className: i("AnagramHelper__clickableWord"), clue: t.clue, onClick: (_) => H(_), splitWords: !d } ) ] }), /* @__PURE__ */ u( xt, { cells: o, letters: f, separators: s, shuffling: d } ) ] }) ] } ); } const Le = typeof window < "u" ? De : le; function Ee(e) { const t = Se(() => { throw new Error("Cannot call an event handler while rendering."); }); return Le(() => { t.current = e; }, [e]), ne((...o) => { var s; return (s = t.current) == null ? void 0 : s.call(t, ...o); }, [t]); } function Ne(e, t, o, s) { const n = Se(t); Le(() => { n.current = t; }, [t]), le(() => { const r = (o == null ? void 0 : o.current) ?? window; if (!(r && r.addEventListener)) return; const i = (c) => { n.current(c); }; return r.addEventListener(e, i, s), () => { r.removeEventListener(e, i, s); }; }, [e, o, s]); } const de = typeof window > "u"; function At(e, t, o = {}) { const { initializeWithValue: s = !0 } = o, n = ne( (v) => o.serializer ? o.serializer(v) : JSON.stringify(v), [o] ), r = ne( (v) => { if (o.deserializer) return o.deserializer(v); if (v === "undefined") return; const C = t instanceof Function ? t() : t; let l; try { l = JSON.parse(v); } catch (a) { return console.error("Error parsing JSON:", a), C; } return l; }, [o, t] ), i = ne(() => { const v = t instanceof Function ? t() : t; if (de) return v; try { const C = window.localStorage.getItem(e); return C ? r(C) : v; } catch (C) { return console.warn(`Error reading localStorage key “${e}”:`, C), v; } }, [t, e, r]), [c, p] = ye(() => s ? i() : t instanceof Function ? t() : t), f = Ee((v) => { de && console.warn( `Tried setting localStorage key “${e}” even though environment is not a client` ); try { const C = v instanceof Function ? v(i()) : v; window.localStorage.setItem(e, n(C)), p(C), window.dispatchEvent(new StorageEvent("local-storage", { key: e })); } catch (C) { console.warn(`Error setting localStorage key “${e}”:`, C); } }), h = Ee(() => { de && console.warn( `Tried removing localStorage key “${e}” even though environment is not a client` ); const v = t instanceof Function ? t() : t; window.localStorage.removeItem(e), p(v), window.dispatchEvent(new StorageEvent("local-storage", { key: e })); }); le(() => { p(i()); }, [e]); const d = ne( (v) => { v.key && v.key !== e || p(i()); }, [e, i] ); return Ne("storage", d), Ne("local-storage", d), [c, f, h]; } const It = 10; function fe({ buttonText: e, onCancel: t, onConfirm: o, timeout: s = It }) { if (s <= 0) throw new Error("Confirm should have a timeout greater than zero"); const n = O(), [r, i] = x.useState(s); return x.useEffect(() => { const c = setTimeout(() => { r <= 1 ? t() : i((p) => p - 1); }, 1e3); return function() { clearTimeout(c); }; }, [r]), /* @__PURE__ */ $("div", { className: n("Confirm"), children: [ /* @__PURE__ */ $("div", { className: n("Confirm__buttonContainer"), children: [ /* @__PURE__ */ u(ee, { onClick: t, variant: "outlined", children: "Cancel" }), /* @__PURE__ */ u(ee, { className: n("Confirm__button"), onClick: o, children: e }) ] }), /* @__PURE__ */ $("span", { className: n("Confirm__timeout"), children: [ "This will automatically cancel in ", r ] }) ] }); } function $t({ 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 Rt({ className: e, id: t, menu: o, text: s }) { if (o.length < 2) throw new Error("DropdownButton should have at least 2 menu items"); const n = O(), r = x.useRef(null), i = x.useRef(null), c = x.useRef(null), [p, f] = x.useState(!1); x.useEffect(() => { const d = (v) => { const C = r.current !== null && !r.current.contains(v.target); p && C && f(!1); }; return document.addEventListener("click", d), function() { document.removeEventListener("click", d); }; }, [p]); const h = () => { if (c.current !== null && i.current !== null && (c.current.style.marginTop = "", !p)) { const d = c.current.getBoundingClientRect(); if (!We(d)) { const C = d.height + i.current.clientHeight + 10; c.current.style.marginTop = `-${C}px`; } } f((d) => !d); }; return /* @__PURE__ */ $( "div", { className: ue( n("DropdownButton", p ? "DropdownButton--expanded" : null), e ), ref: r, children: [ /* @__PURE__ */ $( "button", { "aria-controls": t !== void 0 ? `${t}-listbox` : void 0, "aria-expanded": p ? "true" : "false", "aria-haspopup": "true", className: n("DropdownButton__button"), id: t, onClick: h, ref: i, type: "button", children: [ /* @__PURE__ */ u("span", { children: s }), /* @__PURE__ */ u($t, { className: n("DropdownButton__dropdownButtonIcon") }) ] } ), /* @__PURE__ */ u( "ul", { "aria-label": `${s} menu`, className: n("DropdownButton__menu"), id: t !== void 0 ? `${t}-listbox` : void 0, ref: c, role: "listbox", children: o.map((d) => /* @__PURE__ */ u("li", { children: /* @__PURE__ */ u( "button", { className: n("DropdownButton__menuItem"), disabled: d.disabled, onClick: () => { d.onClick(), f(!1); }, type: "button", children: d.text } ) }, d.text)) } ) ] } ); } const pe = x.memo(Rt); function Gt({ cells: e, clues: t, gridCols: o, gridRows: s, onAnagramHelperClick: n, onCellChange: r, onComplete: i, setGuessGrid: c, solutionsAvailable: p }) { const f = O(), h = e.find((w) => w.selected), d = t.find((w) => w.selected), [v, C] = x.useState(!1), [l, a] = x.useState(!1), [G, H] = x.useState(!1), _ = V((w) => w.answerAll), R = V((w) => w.setCells), M = V((w) => w.checkComplete), L = Y((w) => w.answerAll), k = Y((w) => w.answerSome), B = (w) => { c($e(o, s, w)); }, j = (w, g) => { ct(w, g).forEach((A) => { const U = t.find((q) => q.id === A); if (U) { const q = z(U, g); k(U.group, q); } }); }, W = (w, g) => { r !== void 0 && w.guess !== g && r({ pos: w.pos, guess: g, previousGuess: w.guess }); }, F = [ { disabled: h === void 0, onClick: () => { if (h !== void 0 && h.guess !== h.val) { W(h, void 0); const w = ce( { ...h, guess: void 0 }, e ); R(w), k(h.clueIds, !1), B(w); } }, text: "Check letter" }, { disabled: d === void 0, onClick: () => { if (d !== void 0) { r !== void 0 && ie(d.group, e).forEach((S) => { S.guess !== void 0 && S.val !== S.guess && W(S, void 0); }); const w = e.map((g) => d.group.filter( (A) => g.clueIds.includes(A) ).length > 0 ? { ...g, guess: g.guess === g.val ? g.val : void 0 } : g); R(w), j(d, w), B(w); } }, text: "Check word" }, { onClick: () => C(!0), text: "Check grid" } ], Z = [ { disabled: h === void 0, onClick: () => { if (h === void 0 || h.guess === h.val) return; W(h, h.val); const w = ce( { ...h, guess: h.val }, e ); R(w), i !== void 0 && M() === !0 && i(), h.clueIds.forEach((g) => { const S = t.find((U) => U.id === g); z(S, w) && k(S.group, !0); }), B(w); }, text: "Reveal letter" }, { disabled: d === void 0, onClick: () => { if (d === void 0) return; r !== void 0 && ie(d.group, e).forEach((S) => { S.val !== S.guess && W(S, S.val); }); const w = e.map((g) => d.group.filter( (A) => g.clueIds.includes(A) ).length > 0 ? { ...g, guess: g.val } : g); R(w), i !== void 0 && M() === !0 && i(), j(d, w), B(w); }, text: "Reveal word" }, { onClick: () => a(!0), text: "Reveal grid" } ], oe = [ { disabled: d === void 0, onClick: () => { if (d !== void 0) { const w = e.map((g) => { const S = d.group.filter( (A) => g.clueIds.includes(A) ); if (S.length > 0) { if (g.clueIds.length === 1) return W(g, void 0), { ...g, guess: void 0 }; const U = S[0].includes("across"); if (rt(e, g, U)) return W(g, void 0), { ...g, guess: void 0 }; } return g; }); R(w), k(d.group, !1), B(w); } }, text: "Clear word" }, { onClick: () => H(!0), text: "Clear grid" } ]; return v ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( fe, { buttonText: "Confirm check grid", onCancel: () => C(!1), onConfirm: () => { r !== void 0 && e.forEach((g) => { g.guess !== void 0 && g.val !== g.guess && W(g, void 0); }); const w = e.map((g) => ({ ...g, guess: g.guess === g.val ? g.val : void 0 })); R(w), t.forEach((g) => { const S = z(g, w); k(g.group, S); }), C(!1), B(w); } } ) }) : l ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( fe, { buttonText: "Confirm reveal grid", onCancel: () => a(!1), onConfirm: () => { r !== void 0 && e.forEach((g) => { g.val !== g.guess && W(g, g.val); }), _(!0), L(!0), a(!1), i !== void 0 && M() === !0 && i(); const w = e.map((g) => ({ ...g, guess: g.val })); B(w); } } ) }) : G ? /* @__PURE__ */ u("div", { className: f("Controls"), children: /* @__PURE__ */ u( fe, { buttonText: "Confirm clear grid", onCancel: () => H(!1), onConfirm: () => { r !== void 0 && e.forEach((w) => { w.guess !== void 0 && W(w, void 0); }), _(!1), L(!1), H(!1), B([]); } } ) }) : /* @__PURE__ */ $("div", { className: f("Controls"), children: [ p ? /* @__PURE__ */ $(te, { 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: oe, text: "Clear" }), /* @__PURE__ */ u("div", { className: f("Controls__buttonContainer"), children: /* @__PURE__ */ u( ee, { ariaLabel: "Anagram helper", disabled: d === void 0, id: "anagram-control", onClick: n, children: /* @__PURE__ */ u("span", {}) } ) }) ] }); } function Lt() { const [e, t] = ye( () => typeof window < "u" ? window.location.hash : "" ); return le(() => { t(window.location.hash); const s = () => { t(window.location.hash); }; return window.addEventListener("hashchange", s), () => window.removeEventListener("hashchange", s); }, []), [e, (s) => { typeof window < "u" && s !== e && (window.location.hash = s); }]; } function Dt({ allowedHtmlTags: e, allowMissingSolutions: t, cellMatcher: o, cellSize: s, data: n, id: r, loadGrid: i, onCellChange: c, onCellFocus: p, onComplete: f, saveGrid: h, stickyClue: d }) { const v = O(), [C, l] = At( `crosswords.${r}`, Ie(n.dimensions.cols, n.dimensions.rows) ), [a] = Lt(), [G, H] = x.useState(!1), [_, R] = x.useState(null), M = x.useRef(null), L = V((N) => N.cells), k = Y((N) =>