From 1e71255371702861e23b07d258c1389471a367c8 Mon Sep 17 00:00:00 2001 From: Joshua Seigler <2583159+seigler@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:12:23 -0700 Subject: [PATCH] WIP --- package-lock.json | 82 +++++++--------------- package.json | 4 +- src/App.tsx | 153 +++++------------------------------------- src/Dashboard.tsx | 36 ++++++++++ src/Puzzle.tsx | 134 ++++++++++++++++++++++++++++++++++++ src/index.tsx | 19 +++++- src/useAppModel.ts | 148 ++++------------------------------------ src/usePuzzleModel.ts | 145 +++++++++++++++++++++++++++++++++++++++ vite.config.ts | 3 +- 9 files changed, 389 insertions(+), 335 deletions(-) create mode 100644 src/Dashboard.tsx create mode 100644 src/Puzzle.tsx create mode 100644 src/usePuzzleModel.ts diff --git a/package-lock.json b/package-lock.json index 6ebb233..6b0ffb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,12 @@ "license": "MIT", "dependencies": { "@solid-primitives/storage": "^4.3.1", + "@solidjs/router": "^0.15.3", "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-store": "^2.2.0", - "solid-js": "^1.9.3" + "solid-js": "^1.9.3", + "vite-plugin-inline-css-modules": "^0.0.8" }, "devDependencies": { "@tauri-apps/cli": "^2", @@ -299,7 +301,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -316,7 +317,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -333,7 +333,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -350,7 +349,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -367,7 +365,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -384,7 +381,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -401,7 +397,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -418,7 +413,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -435,7 +429,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -452,7 +445,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -469,7 +461,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -486,7 +477,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -503,7 +493,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -520,7 +509,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -537,7 +525,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -554,7 +541,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -571,7 +557,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -588,7 +573,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -605,7 +589,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -622,7 +605,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -639,7 +621,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -656,7 +637,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -673,7 +653,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -690,7 +669,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -707,7 +685,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -777,7 +754,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -791,7 +767,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -805,7 +780,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -819,7 +793,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -833,7 +806,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -847,7 +819,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -861,7 +832,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -875,7 +845,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -889,7 +858,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -903,7 +871,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -917,7 +884,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -931,7 +897,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -945,7 +910,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -959,7 +923,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -973,7 +936,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -987,7 +949,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1001,7 +962,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1015,7 +975,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1029,7 +988,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1043,7 +1001,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1080,6 +1037,15 @@ "solid-js": "^1.6.12" } }, + "node_modules/@solidjs/router": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@solidjs/router/-/router-0.15.3.tgz", + "integrity": "sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw==", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.8.6" + } + }, "node_modules/@tauri-apps/api": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.4.1.tgz", @@ -1374,7 +1340,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, "license": "MIT" }, "node_modules/babel-plugin-jsx-dom-expressions": { @@ -1530,7 +1495,6 @@ "version": "0.25.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -1581,7 +1545,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1702,7 +1665,6 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, "funding": [ { "type": "github", @@ -1741,14 +1703,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -1777,7 +1737,6 @@ "version": "4.38.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.38.0.tgz", "integrity": "sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.7" @@ -1874,7 +1833,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -1933,10 +1891,9 @@ "license": "ISC" }, "node_modules/vite": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz", - "integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==", - "dev": true, + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", + "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -2004,6 +1961,15 @@ } } }, + "node_modules/vite-plugin-inline-css-modules": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/vite-plugin-inline-css-modules/-/vite-plugin-inline-css-modules-0.0.8.tgz", + "integrity": "sha512-RXpGZJ0YW69HjkOBw/1xVeVk677wZ/OPmAbvln2eFG+9dFfq9AQapCb/iZZey/oK8psoHiwb5x2HXv5FM2UPcw==", + "license": "MIT", + "peerDependencies": { + "vite": ">2.0.0-0" + } + }, "node_modules/vite-plugin-solid": { "version": "2.11.6", "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.6.tgz", diff --git a/package.json b/package.json index cabca8c..5dd1a79 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,12 @@ "license": "MIT", "dependencies": { "@solid-primitives/storage": "^4.3.1", + "@solidjs/router": "^0.15.3", "@tauri-apps/api": "^2", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-store": "^2.2.0", - "solid-js": "^1.9.3" + "solid-js": "^1.9.3", + "vite-plugin-inline-css-modules": "^0.0.8" }, "devDependencies": { "@tauri-apps/cli": "^2", diff --git a/src/App.tsx b/src/App.tsx index abfa750..49cc4a2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,141 +1,18 @@ -import { For } from "solid-js"; -import "./App.css"; -import useAppModel from "./useAppModel"; -import FitText from "./FitText"; +import { type JSX } from "solid-js" -// TODO -// add routing -// make overview page with calendar list of puzzles -// show solved / aced / busted -// make detail page with puzzle id in path -// add nav links - -function App() { - const { - connections, - store, - setStore, - handleGuess, - handlePinUnpin, - handleSelectGame, - handleShuffle, - handleDeselect, - getFromPuzzle, - } = useAppModel(); - - return ( -
-
-
-

Slick Connections

-
- - - -
-
-
Create four groups of four words!
- - {({ group, level, members }) => ( -
-
-
{group}
-
{members.join(", ")}
-
-
- )} -
- - {(row) => ( -
- {[0, 1, 2, 3].map((col) => { - const index = 4 * row + col; - const answer = () => getFromPuzzle(index).answer; - return ( - - ); - })} -
- )} -
-
- - - - -
-
- { store.solvedGroups.length === 4 &&
} -
- ); +type AppProps = { + children?: JSX.Element } -export default App; +export default function App({ children }: AppProps) { + return ( + <> +
+

Slick Connections

+
+
+ {children} +
+ + ) +} diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx new file mode 100644 index 0000000..817e21c --- /dev/null +++ b/src/Dashboard.tsx @@ -0,0 +1,36 @@ +import { For } from "solid-js"; +import useAppModel from "./useAppModel"; +import { A } from "@solidjs/router"; +import { css } from 'vite-plugin-inline-css-modules' + +const styles = css` + calendar: { + display: "grid", + gridTemplateColumns: "repeat(7, 1fr)", + }, + entry: (solved: boolean) => { + return { + borderRadius: "50%", + backgroundColor: solved ? "green" : "gray", + } + }, +` + +export default function Dashboard() { + const { connections, store } = useAppModel(); + return ( +
+ + {(item) => { + const isSolved = store.solutions[item.id] !== undefined; + return ( + + ); + }} + +
+ ); +} diff --git a/src/Puzzle.tsx b/src/Puzzle.tsx new file mode 100644 index 0000000..fca94a7 --- /dev/null +++ b/src/Puzzle.tsx @@ -0,0 +1,134 @@ +import { For } from "solid-js"; +import "./App.css"; +import usePuzzleModel from "./usePuzzleModel"; +import FitText from "./FitText"; +import { useNavigate, useParams } from "@solidjs/router"; + +// TODO +// add routing +// make overview page with calendar list of puzzles +// show solved / aced / busted +// make detail page with puzzle id in path +// add nav links + +function Puzzle() { + const params = useParams<{ id: string }>() + const id = () => parseInt(params.id); + + const { + connections, + store, + setStore, + handleGuess, + handlePinUnpin, + handleShuffle, + handleDeselect, + getFromPuzzle, + } = usePuzzleModel(id); + + const navigate = useNavigate() + + return ( +
+
+
+

Slick Connections

+
+ + +
+
+
Create four groups of four words!
+ + {({ group, level, members }) => ( +
+
+
{group}
+
{members.join(", ")}
+
+
+ )} +
+ + {(row) => ( +
+ {[0, 1, 2, 3].map((col) => { + const index = 4 * row + col; + const answer = () => getFromPuzzle(index).answer; + return ( + + ); + })} +
+ )} +
+
+ + + + +
+
+ {store.solvedGroups.length === 4 &&
} +
+ ); +} + +export default Puzzle; diff --git a/src/index.tsx b/src/index.tsx index 26b5517..b971442 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,22 @@ /* @refresh reload */ import { render } from "solid-js/web"; import App from "./App"; +import { Route, Router } from "@solidjs/router"; +import Dashboard from "./Dashboard"; +import Puzzle from "./Puzzle"; -render(() => , document.getElementById("root") as HTMLElement); +render( + () => ( + + + + + ), + document.getElementById("root") as HTMLElement +); diff --git a/src/useAppModel.ts b/src/useAppModel.ts index 70bc249..450dd62 100644 --- a/src/useAppModel.ts +++ b/src/useAppModel.ts @@ -1,24 +1,15 @@ import { makePersisted } from "@solid-primitives/storage"; import connections from "./assets/connections.json"; -import { shuffleArray } from "./utils"; import { createStore } from "solid-js/store"; import { tauriStorage } from "@solid-primitives/storage/tauri"; -type Connection = (typeof connections)[number]; -type Answer = Connection["answers"][number]; - -function fromIndex(index: number): [number, number] { - const col = index % 4; - const row = (index - col) / 4; - return [row, col]; +type Solution = { + id: number + mistakes: number } type AppStore = { - puzzleId: number; - pinnedCount: number; - selected: number[]; - solvedGroups: Answer[]; - puzzle: number[]; + solutions: Solution[] }; export default function useAppModel() { @@ -26,138 +17,23 @@ export default function useAppModel() { "__TAURI_INTERNALS__" in window ? tauriStorage("AppStore") : localStorage; const [store, setStore] = makePersisted( createStore({ - puzzleId: 1, - pinnedCount: 0, - selected: [], - solvedGroups: [], - puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), + solutions: [] }), { name: "slick-connections", storage, - serialize(data) { - return JSON.stringify({ - ...data, - selected: [], - }); - }, } ); - - const answers = (): Answer[] => { - return connections.find((x) => x.id === store.puzzleId)!.answers; - }; - - const handleSelectGame = (newId: number) => { - setStore({ - puzzleId: newId, - selected: [], - pinnedCount: 0, - puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), - solvedGroups: [], - }); - }; - - const getFromPuzzle = (index: number) => { - const puzzleIndex = store.puzzle[index]; - const [groupIndex, memberIndex] = fromIndex(puzzleIndex); - const group = answers()[groupIndex]; - return { - group: group.group, - level: group.level, - answer: answers()[groupIndex].members[memberIndex], - }; - }; - - const handleGuess = () => { - const selected = store.puzzle.length === 4 ? [0, 1, 2, 3] : store.selected; - const selectedAnswers = selected.map((x) => getFromPuzzle(x)); - const { level } = selectedAnswers[0]; - const isCorrect = selectedAnswers.every((x) => x.level === level); - if (!isCorrect) { - // TODO you got it wrong - alert("wrong"); - return; - } - const selectedPinnedCount = selected.reduce( - (acc, cur) => acc + (cur < store.pinnedCount ? 1 : 0), - 0 - ); - setStore({ - pinnedCount: store.pinnedCount - selectedPinnedCount, - puzzle: store.puzzle.filter((x) => - selected.every((s) => store.puzzle[s] !== x) - ), - selected: [], - }); - const newSolvedGroup = answers().find((x) => x.level === level); - if (newSolvedGroup != null) { - setStore({ - solvedGroups: [...store.solvedGroups, newSolvedGroup], - }); - } - if (store.puzzle.length === 0) { - // completely solved! - } - }; - - const handleShuffle = () => { - const pinned = store.puzzle.slice(0, store.pinnedCount); - const toShuffle = store.puzzle.slice(store.pinnedCount); - setStore({ - puzzle: [...pinned, ...shuffleArray(toShuffle)], - }); - }; - - const handleDeselect = () => { - setStore({ - selected: [], - }); - }; - - const handlePinUnpin = () => { - if (store.selected.every((x) => x < store.pinnedCount)) { - // we are unpinning - const puzzleStart = Array.from({ length: store.pinnedCount }, (_, i) => i) - .filter((x) => !store.selected.includes(x)) - .map((x) => store.puzzle[x]); - const puzzleMiddle = store.selected.map((x) => store.puzzle[x]); - const puzzleEnd = store.puzzle.slice(store.pinnedCount); - const newPuzzle = [...puzzleStart, ...puzzleMiddle, ...puzzleEnd]; - setStore({ - pinnedCount: store.pinnedCount - store.selected.length, - selected: [], - puzzle: newPuzzle, - }); - return; - } - // we are pinning - const puzzleStart = store.puzzle.slice(0, store.pinnedCount); - const puzzleMid = store.selected - .filter((x) => x >= store.pinnedCount) - .map((x) => store.puzzle[x]); - const puzzleEnd = Array.from( - { length: store.puzzle.length - store.pinnedCount }, - (_, i) => i + store.pinnedCount - ) - .filter((x) => !store.selected.includes(x)) - .map((x) => store.puzzle[x]); - setStore({ - pinnedCount: puzzleStart.length + puzzleMid.length, - selected: [], - puzzle: [...puzzleStart, ...puzzleMid, ...puzzleEnd], - }); - }; + function setSolution(id: number, mistakes: number) { + setStore("solutions", id, { + id, + mistakes + }) + } return { connections, store, - setStore, - handleGuess, - handlePinUnpin, - handleSelectGame, - handleShuffle, - handleDeselect, - getFromPuzzle, + setSolution, }; } diff --git a/src/usePuzzleModel.ts b/src/usePuzzleModel.ts new file mode 100644 index 0000000..b6eae5c --- /dev/null +++ b/src/usePuzzleModel.ts @@ -0,0 +1,145 @@ +import { Accessor, createEffect } from "solid-js"; +import connections from "./assets/connections.json"; +import { shuffleArray } from "./utils"; +import { createStore } from "solid-js/store"; + +type Connection = (typeof connections)[number]; +type Answer = Connection["answers"][number]; + +function fromIndex(index: number): [number, number] { + const col = index % 4; + const row = (index - col) / 4; + return [row, col]; +} + +type AppStore = { + pinnedCount: number; + selected: number[]; + solvedGroups: Answer[]; + puzzle: number[]; +}; + +export default function usePuzzleModel(id: Accessor) { + const [store, setStore] = createStore({ + pinnedCount: 0, + selected: [], + solvedGroups: [], + puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), + }); + + createEffect(() => { + id() + setStore({ + pinnedCount: 0, + selected: [], + solvedGroups: [], + puzzle: shuffleArray(Array.from({ length: 16 }, (_, i) => i)), + }); + }) + + const answers = (): Answer[] => { + return connections.find((x) => x.id === id())!.answers; + }; + + const getFromPuzzle = (index: number) => { + const puzzleIndex = store.puzzle[index]; + const [groupIndex, memberIndex] = fromIndex(puzzleIndex); + const group = answers()[groupIndex]; + return { + group: group.group, + level: group.level, + answer: answers()[groupIndex].members[memberIndex], + }; + }; + + const handleGuess = () => { + const selected = store.puzzle.length === 4 ? [0, 1, 2, 3] : store.selected; + const selectedAnswers = selected.map((x) => getFromPuzzle(x)); + const { level } = selectedAnswers[0]; + const isCorrect = selectedAnswers.every((x) => x.level === level); + if (!isCorrect) { + // TODO you got it wrong + alert("wrong"); + return; + } + const selectedPinnedCount = selected.reduce( + (acc, cur) => acc + (cur < store.pinnedCount ? 1 : 0), + 0 + ); + setStore({ + pinnedCount: store.pinnedCount - selectedPinnedCount, + puzzle: store.puzzle.filter((x) => + selected.every((s) => store.puzzle[s] !== x) + ), + selected: [], + }); + const newSolvedGroup = answers().find((x) => x.level === level); + if (newSolvedGroup != null) { + setStore({ + solvedGroups: [...store.solvedGroups, newSolvedGroup], + }); + } + if (store.puzzle.length === 0) { + // completely solved! + } + }; + + const handleShuffle = () => { + const pinned = store.puzzle.slice(0, store.pinnedCount); + const toShuffle = store.puzzle.slice(store.pinnedCount); + setStore({ + puzzle: [...pinned, ...shuffleArray(toShuffle)], + }); + }; + + const handleDeselect = () => { + setStore({ + selected: [], + }); + }; + + const handlePinUnpin = () => { + if (store.selected.every((x) => x < store.pinnedCount)) { + // we are unpinning + const puzzleStart = Array.from({ length: store.pinnedCount }, (_, i) => i) + .filter((x) => !store.selected.includes(x)) + .map((x) => store.puzzle[x]); + const puzzleMiddle = store.selected.map((x) => store.puzzle[x]); + const puzzleEnd = store.puzzle.slice(store.pinnedCount); + const newPuzzle = [...puzzleStart, ...puzzleMiddle, ...puzzleEnd]; + setStore({ + pinnedCount: store.pinnedCount - store.selected.length, + selected: [], + puzzle: newPuzzle, + }); + return; + } + // we are pinning + const puzzleStart = store.puzzle.slice(0, store.pinnedCount); + const puzzleMid = store.selected + .filter((x) => x >= store.pinnedCount) + .map((x) => store.puzzle[x]); + const puzzleEnd = Array.from( + { length: store.puzzle.length - store.pinnedCount }, + (_, i) => i + store.pinnedCount + ) + .filter((x) => !store.selected.includes(x)) + .map((x) => store.puzzle[x]); + setStore({ + pinnedCount: puzzleStart.length + puzzleMid.length, + selected: [], + puzzle: [...puzzleStart, ...puzzleMid, ...puzzleEnd], + }); + }; + + return { + connections, + store, + setStore, + handleGuess, + handlePinUnpin, + handleShuffle, + handleDeselect, + getFromPuzzle, + }; +} diff --git a/vite.config.ts b/vite.config.ts index 80474aa..b417427 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,12 +1,13 @@ import { defineConfig } from "vite"; import solid from "vite-plugin-solid"; +import vitePluginInlineCSSModules from 'vite-plugin-inline-css-modules' // @ts-expect-error process is a nodejs global const host = process.env.TAURI_DEV_HOST; // https://vitejs.dev/config/ export default defineConfig(async () => ({ - plugins: [solid()], + plugins: [solid(), vitePluginInlineCSSModules()], // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` //