enhancement: apply prettier

This commit is contained in:
haithamleo 2022-01-16 15:11:27 +04:00
parent c8e415c737
commit 409afb2b7d
31 changed files with 13357 additions and 13343 deletions

2
.prettierrc Normal file
View file

@ -0,0 +1,2 @@
singleQuote: true
semi: false

17
package-lock.json generated
View file

@ -18,6 +18,7 @@
"@types/react": "^17.0.38", "@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11", "@types/react-dom": "^17.0.11",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"prettier": "^2.5.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
@ -12628,6 +12629,17 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/pretty-bytes": { "node_modules/pretty-bytes": {
"version": "5.6.0", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@ -24849,6 +24861,11 @@
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
}, },
"prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg=="
},
"pretty-bytes": { "pretty-bytes": {
"version": "5.6.0", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",

View file

@ -46,6 +46,7 @@
"devDependencies": { "devDependencies": {
"autoprefixer": "^10.4.2", "autoprefixer": "^10.4.2",
"postcss": "^8.4.5", "postcss": "^8.4.5",
"tailwindcss": "^3.0.12" "tailwindcss": "^3.0.12",
"prettier": "^2.5.1"
} }
} }

View file

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react'
import { render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react'
import App from './App'; import App from './App'
test('renders learn react link', () => { test('renders learn react link', () => {
render(<App />); render(<App />)
const linkElement = screen.getByText(/learn react/i); const linkElement = screen.getByText(/learn react/i)
expect(linkElement).toBeInTheDocument(); expect(linkElement).toBeInTheDocument()
}); })

View file

@ -1,77 +1,77 @@
import { InformationCircleIcon } from "@heroicons/react/outline"; import { InformationCircleIcon } from '@heroicons/react/outline'
import { useState, useEffect } from "react"; import { useState, useEffect } from 'react'
import { Alert } from "./components/alerts/Alert"; import { Alert } from './components/alerts/Alert'
import { Grid } from "./components/grid/Grid"; import { Grid } from './components/grid/Grid'
import { Keyboard } from "./components/keyboard/Keyboard"; import { Keyboard } from './components/keyboard/Keyboard'
import { AboutModal } from "./components/modals/AboutModal"; import { AboutModal } from './components/modals/AboutModal'
import { InfoModal } from "./components/modals/InfoModal"; import { InfoModal } from './components/modals/InfoModal'
import { WinModal } from "./components/modals/WinModal"; import { WinModal } from './components/modals/WinModal'
import { isWordInWordList, isWinningWord, solution } from "./lib/words"; import { isWordInWordList, isWinningWord, solution } from './lib/words'
import { import {
loadGameStateFromLocalStorage, loadGameStateFromLocalStorage,
saveGameStateToLocalStorage, saveGameStateToLocalStorage,
} from "./lib/localStorage"; } from './lib/localStorage'
function App() { function App() {
const [guesses, setGuesses] = useState<string[]>(() => { const [guesses, setGuesses] = useState<string[]>(() => {
const loaded = loadGameStateFromLocalStorage(); const loaded = loadGameStateFromLocalStorage()
return loaded?.solution === solution ? loaded.guesses : []; return loaded?.solution === solution ? loaded.guesses : []
}); })
const [currentGuess, setCurrentGuess] = useState(""); const [currentGuess, setCurrentGuess] = useState('')
const [isGameWon, setIsGameWon] = useState(false); const [isGameWon, setIsGameWon] = useState(false)
const [isWinModalOpen, setIsWinModalOpen] = useState(false); const [isWinModalOpen, setIsWinModalOpen] = useState(false)
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false); const [isInfoModalOpen, setIsInfoModalOpen] = useState(false)
const [isAboutModalOpen, setIsAboutModalOpen] = useState(false); const [isAboutModalOpen, setIsAboutModalOpen] = useState(false)
const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false); const [isWordNotFoundAlertOpen, setIsWordNotFoundAlertOpen] = useState(false)
const [isGameLost, setIsGameLost] = useState(false); const [isGameLost, setIsGameLost] = useState(false)
const [shareComplete, setShareComplete] = useState(false); const [shareComplete, setShareComplete] = useState(false)
useEffect(() => { useEffect(() => {
saveGameStateToLocalStorage({ guesses, solution }); saveGameStateToLocalStorage({ guesses, solution })
}, [guesses]); }, [guesses])
useEffect(() => { useEffect(() => {
if (isGameWon) { if (isGameWon) {
setIsWinModalOpen(true); setIsWinModalOpen(true)
} }
}, [isGameWon]); }, [isGameWon])
const onChar = (value: string) => { const onChar = (value: string) => {
if (currentGuess.length < 5 && guesses.length < 6) { if (currentGuess.length < 5 && guesses.length < 6) {
setCurrentGuess(`${currentGuess}${value}`); setCurrentGuess(`${currentGuess}${value}`)
}
} }
};
const onDelete = () => { const onDelete = () => {
setCurrentGuess(currentGuess.slice(0, -1)); setCurrentGuess(currentGuess.slice(0, -1))
}; }
const onEnter = () => { const onEnter = () => {
if (!isWordInWordList(currentGuess)) { if (!isWordInWordList(currentGuess)) {
setIsWordNotFoundAlertOpen(true); setIsWordNotFoundAlertOpen(true)
return setTimeout(() => { return setTimeout(() => {
setIsWordNotFoundAlertOpen(false); setIsWordNotFoundAlertOpen(false)
}, 2000); }, 2000)
} }
const winningWord = isWinningWord(currentGuess); const winningWord = isWinningWord(currentGuess)
if (currentGuess.length === 5 && guesses.length < 6 && !isGameWon) { if (currentGuess.length === 5 && guesses.length < 6 && !isGameWon) {
setGuesses([...guesses, currentGuess]); setGuesses([...guesses, currentGuess])
setCurrentGuess(""); setCurrentGuess('')
if (winningWord) { if (winningWord) {
return setIsGameWon(true); return setIsGameWon(true)
} }
if (guesses.length === 5) { if (guesses.length === 5) {
setIsGameLost(true); setIsGameLost(true)
return setTimeout(() => { return setTimeout(() => {
setIsGameLost(false); setIsGameLost(false)
}, 2000); }, 2000)
}
} }
} }
};
return ( return (
<div className="py-8 max-w-7xl mx-auto sm:px-6 lg:px-8"> <div className="py-8 max-w-7xl mx-auto sm:px-6 lg:px-8">
@ -104,11 +104,11 @@ function App() {
handleClose={() => setIsWinModalOpen(false)} handleClose={() => setIsWinModalOpen(false)}
guesses={guesses} guesses={guesses}
handleShare={() => { handleShare={() => {
setIsWinModalOpen(false); setIsWinModalOpen(false)
setShareComplete(true); setShareComplete(true)
return setTimeout(() => { return setTimeout(() => {
setShareComplete(false); setShareComplete(false)
}, 2000); }, 2000)
}} }}
/> />
<InfoModal <InfoModal
@ -128,7 +128,7 @@ function App() {
About this game About this game
</button> </button>
</div> </div>
); )
} }
export default App; export default App

View file

@ -1,21 +1,21 @@
import { Fragment } from "react"; import { Fragment } from 'react'
import { Transition } from "@headlessui/react"; import { Transition } from '@headlessui/react'
import classNames from "classnames"; import classNames from 'classnames'
type Props = { type Props = {
isOpen: boolean; isOpen: boolean
message: string; message: string
variant?: "success" | "warning"; variant?: 'success' | 'warning'
};
export const Alert = ({ isOpen, message, variant = "warning" }: Props) => {
const classes = classNames(
"fixed top-2.5 left-1/2 transform -translate-x-1/2 max-w-sm w-full shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden",
{
"bg-rose-200": variant === "warning",
"bg-green-200": variant === "success",
} }
);
export const Alert = ({ isOpen, message, variant = 'warning' }: Props) => {
const classes = classNames(
'fixed top-2.5 left-1/2 transform -translate-x-1/2 max-w-sm w-full shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden',
{
'bg-rose-200': variant === 'warning',
'bg-green-200': variant === 'success',
}
)
return ( return (
<Transition <Transition
@ -36,5 +36,5 @@ export const Alert = ({ isOpen, message, variant = "warning" }: Props) => {
</div> </div>
</div> </div>
</Transition> </Transition>
); )
}; }

View file

@ -1,25 +1,25 @@
import { CharStatus } from "../../lib/statuses"; import { CharStatus } from '../../lib/statuses'
import classnames from "classnames"; import classnames from 'classnames'
type Props = { type Props = {
value?: string; value?: string
status?: CharStatus; status?: CharStatus
}; }
export const Cell = ({ value, status }: Props) => { export const Cell = ({ value, status }: Props) => {
const classes = classnames( const classes = classnames(
"w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded", 'w-14 h-14 border-solid border-2 flex items-center justify-center mx-0.5 text-lg font-bold rounded',
{ {
"bg-white border-slate-200": !status, 'bg-white border-slate-200': !status,
"bg-slate-400 text-white border-slate-400": status === "absent", 'bg-slate-400 text-white border-slate-400': status === 'absent',
"bg-green-500 text-white border-green-500": status === "correct", 'bg-green-500 text-white border-green-500': status === 'correct',
"bg-yellow-500 text-white border-yellow-500": status === "present", 'bg-yellow-500 text-white border-yellow-500': status === 'present',
} }
); )
return ( return (
<> <>
<div className={classes}>{value}</div> <div className={classes}>{value}</div>
</> </>
); )
}; }

View file

@ -1,18 +1,18 @@
import { getGuessStatuses } from "../../lib/statuses"; import { getGuessStatuses } from '../../lib/statuses'
import { Cell } from "./Cell"; import { Cell } from './Cell'
type Props = { type Props = {
guess: string; guess: string
}; }
export const CompletedRow = ({ guess }: Props) => { export const CompletedRow = ({ guess }: Props) => {
const statuses = getGuessStatuses(guess); const statuses = getGuessStatuses(guess)
return ( return (
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
{guess.split("").map((letter, i) => ( {guess.split('').map((letter, i) => (
<Cell key={i} value={letter} status={statuses[i]} /> <Cell key={i} value={letter} status={statuses[i]} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,12 +1,12 @@
import { Cell } from "./Cell"; import { Cell } from './Cell'
type Props = { type Props = {
guess: string; guess: string
}; }
export const CurrentRow = ({ guess }: Props) => { export const CurrentRow = ({ guess }: Props) => {
const splitGuess = guess.split(""); const splitGuess = guess.split('')
const emptyCells = Array.from(Array(5 - splitGuess.length)); const emptyCells = Array.from(Array(5 - splitGuess.length))
return ( return (
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
@ -17,5 +17,5 @@ export const CurrentRow = ({ guess }: Props) => {
<Cell key={i} /> <Cell key={i} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,7 +1,7 @@
import { Cell } from "./Cell"; import { Cell } from './Cell'
export const EmptyRow = () => { export const EmptyRow = () => {
const emptyCells = Array.from(Array(5)); const emptyCells = Array.from(Array(5))
return ( return (
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
@ -9,5 +9,5 @@ export const EmptyRow = () => {
<Cell key={i} /> <Cell key={i} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,15 +1,15 @@
import { CompletedRow } from "./CompletedRow"; import { CompletedRow } from './CompletedRow'
import { CurrentRow } from "./CurrentRow"; import { CurrentRow } from './CurrentRow'
import { EmptyRow } from "./EmptyRow"; import { EmptyRow } from './EmptyRow'
type Props = { type Props = {
guesses: string[]; guesses: string[]
currentGuess: string; currentGuess: string
}; }
export const Grid = ({ guesses, currentGuess }: Props) => { export const Grid = ({ guesses, currentGuess }: Props) => {
const empties = const empties =
guesses.length < 5 ? Array.from(Array(5 - guesses.length)) : []; guesses.length < 5 ? Array.from(Array(5 - guesses.length)) : []
return ( return (
<div className="pb-6"> <div className="pb-6">
@ -21,5 +21,5 @@ export const Grid = ({ guesses, currentGuess }: Props) => {
<EmptyRow key={i} /> <EmptyRow key={i} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,15 +1,15 @@
import { ReactNode } from "react"; import { ReactNode } from 'react'
import classnames from "classnames"; import classnames from 'classnames'
import { KeyValue } from "../../lib/keyboard"; import { KeyValue } from '../../lib/keyboard'
import { CharStatus } from "../../lib/statuses"; import { CharStatus } from '../../lib/statuses'
type Props = { type Props = {
children?: ReactNode; children?: ReactNode
value: KeyValue; value: KeyValue
width?: number; width?: number
status?: CharStatus; status?: CharStatus
onClick: (value: KeyValue) => void; onClick: (value: KeyValue) => void
}; }
export const Key = ({ export const Key = ({
children, children,
@ -19,24 +19,24 @@ export const Key = ({
onClick, onClick,
}: Props) => { }: Props) => {
const classes = classnames( const classes = classnames(
"flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer", 'flex items-center justify-center rounded mx-0.5 text-xs font-bold cursor-pointer',
{ {
"bg-slate-200 hover:bg-slate-300 active:bg-slate-400": !status, 'bg-slate-200 hover:bg-slate-300 active:bg-slate-400': !status,
"bg-slate-400 text-white": status === "absent", 'bg-slate-400 text-white': status === 'absent',
"bg-green-500 hover:bg-green-600 active:bg-green-700 text-white": 'bg-green-500 hover:bg-green-600 active:bg-green-700 text-white':
status === "correct", status === 'correct',
"bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700 text-white": 'bg-yellow-500 hover:bg-yellow-600 active:bg-yellow-700 text-white':
status === "present", status === 'present',
} }
); )
return ( return (
<div <div
style={{ width: `${width}px`, height: "58px" }} style={{ width: `${width}px`, height: '58px' }}
className={classes} className={classes}
onClick={() => onClick(value)} onClick={() => onClick(value)}
> >
{children || value} {children || value}
</div> </div>
); )
}; }

View file

@ -1,87 +1,87 @@
import { KeyValue } from "../../lib/keyboard"; import { KeyValue } from '../../lib/keyboard'
import { getStatuses } from "../../lib/statuses"; import { getStatuses } from '../../lib/statuses'
import { Key } from "./Key"; import { Key } from './Key'
import {useEffect} from "react"; import { useEffect } from 'react'
type Props = { type Props = {
onChar: (value: string) => void; onChar: (value: string) => void
onDelete: () => void; onDelete: () => void
onEnter: () => void; onEnter: () => void
guesses: string[]; guesses: string[]
}; }
export const Keyboard = ({ onChar, onDelete, onEnter, guesses }: Props) => { export const Keyboard = ({ onChar, onDelete, onEnter, guesses }: Props) => {
const charStatuses = getStatuses(guesses); const charStatuses = getStatuses(guesses)
const onClick = (value: KeyValue) => { const onClick = (value: KeyValue) => {
if (value === "ENTER") { if (value === 'ENTER') {
onEnter(); onEnter()
} else if (value === "DELETE") { } else if (value === 'DELETE') {
onDelete(); onDelete()
} else { } else {
onChar(value); onChar(value)
}
} }
};
useEffect(() => { useEffect(() => {
const listener = (e: KeyboardEvent) => { const listener = (e: KeyboardEvent) => {
if(e.code === "Enter") { if (e.code === 'Enter') {
onEnter(); onEnter()
} else if(e.code === "Backspace") { } else if (e.code === 'Backspace') {
onDelete(); onDelete()
} else { } else {
const key = e.key.toUpperCase(); const key = e.key.toUpperCase()
if(key.length === 1 && key >= "A" && key <= "Z") { if (key.length === 1 && key >= 'A' && key <= 'Z') {
onChar(key); onChar(key)
} }
} }
}; }
window.addEventListener("keyup", listener); window.addEventListener('keyup', listener)
return () => { return () => {
window.removeEventListener("keyup", listener); window.removeEventListener('keyup', listener)
}; }
}, [onEnter, onDelete, onChar]); }, [onEnter, onDelete, onChar])
return ( return (
<div> <div>
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
<Key value="Q" onClick={onClick} status={charStatuses["Q"]} /> <Key value="Q" onClick={onClick} status={charStatuses['Q']} />
<Key value="W" onClick={onClick} status={charStatuses["W"]} /> <Key value="W" onClick={onClick} status={charStatuses['W']} />
<Key value="E" onClick={onClick} status={charStatuses["E"]} /> <Key value="E" onClick={onClick} status={charStatuses['E']} />
<Key value="R" onClick={onClick} status={charStatuses["R"]} /> <Key value="R" onClick={onClick} status={charStatuses['R']} />
<Key value="T" onClick={onClick} status={charStatuses["T"]} /> <Key value="T" onClick={onClick} status={charStatuses['T']} />
<Key value="Y" onClick={onClick} status={charStatuses["Y"]} /> <Key value="Y" onClick={onClick} status={charStatuses['Y']} />
<Key value="U" onClick={onClick} status={charStatuses["U"]} /> <Key value="U" onClick={onClick} status={charStatuses['U']} />
<Key value="I" onClick={onClick} status={charStatuses["I"]} /> <Key value="I" onClick={onClick} status={charStatuses['I']} />
<Key value="O" onClick={onClick} status={charStatuses["O"]} /> <Key value="O" onClick={onClick} status={charStatuses['O']} />
<Key value="P" onClick={onClick} status={charStatuses["P"]} /> <Key value="P" onClick={onClick} status={charStatuses['P']} />
</div> </div>
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
<Key value="A" onClick={onClick} status={charStatuses["A"]} /> <Key value="A" onClick={onClick} status={charStatuses['A']} />
<Key value="S" onClick={onClick} status={charStatuses["S"]} /> <Key value="S" onClick={onClick} status={charStatuses['S']} />
<Key value="D" onClick={onClick} status={charStatuses["D"]} /> <Key value="D" onClick={onClick} status={charStatuses['D']} />
<Key value="F" onClick={onClick} status={charStatuses["F"]} /> <Key value="F" onClick={onClick} status={charStatuses['F']} />
<Key value="G" onClick={onClick} status={charStatuses["G"]} /> <Key value="G" onClick={onClick} status={charStatuses['G']} />
<Key value="H" onClick={onClick} status={charStatuses["H"]} /> <Key value="H" onClick={onClick} status={charStatuses['H']} />
<Key value="J" onClick={onClick} status={charStatuses["J"]} /> <Key value="J" onClick={onClick} status={charStatuses['J']} />
<Key value="K" onClick={onClick} status={charStatuses["K"]} /> <Key value="K" onClick={onClick} status={charStatuses['K']} />
<Key value="L" onClick={onClick} status={charStatuses["L"]} /> <Key value="L" onClick={onClick} status={charStatuses['L']} />
</div> </div>
<div className="flex justify-center"> <div className="flex justify-center">
<Key width={65.4} value="ENTER" onClick={onClick}> <Key width={65.4} value="ENTER" onClick={onClick}>
Enter Enter
</Key> </Key>
<Key value="Z" onClick={onClick} status={charStatuses["Z"]} /> <Key value="Z" onClick={onClick} status={charStatuses['Z']} />
<Key value="X" onClick={onClick} status={charStatuses["X"]} /> <Key value="X" onClick={onClick} status={charStatuses['X']} />
<Key value="C" onClick={onClick} status={charStatuses["C"]} /> <Key value="C" onClick={onClick} status={charStatuses['C']} />
<Key value="V" onClick={onClick} status={charStatuses["V"]} /> <Key value="V" onClick={onClick} status={charStatuses['V']} />
<Key value="B" onClick={onClick} status={charStatuses["B"]} /> <Key value="B" onClick={onClick} status={charStatuses['B']} />
<Key value="N" onClick={onClick} status={charStatuses["N"]} /> <Key value="N" onClick={onClick} status={charStatuses['N']} />
<Key value="M" onClick={onClick} status={charStatuses["M"]} /> <Key value="M" onClick={onClick} status={charStatuses['M']} />
<Key width={65.4} value="DELETE" onClick={onClick}> <Key width={65.4} value="DELETE" onClick={onClick}>
Delete Delete
</Key> </Key>
</div> </div>
</div> </div>
); )
}; }

View file

@ -1,23 +1,23 @@
import { CharStatus } from "../../lib/statuses"; import { CharStatus } from '../../lib/statuses'
import classnames from "classnames"; import classnames from 'classnames'
type Props = { type Props = {
status: CharStatus; status: CharStatus
}; }
export const MiniCell = ({ status }: Props) => { export const MiniCell = ({ status }: Props) => {
const classes = classnames( const classes = classnames(
"w-10 h-10 border-solid border-2 border-slate-200 flex items-center justify-center mx-0.5 text-lg font-bold rounded", 'w-10 h-10 border-solid border-2 border-slate-200 flex items-center justify-center mx-0.5 text-lg font-bold rounded',
{ {
"bg-white": status === "absent", 'bg-white': status === 'absent',
"bg-green-500": status === "correct", 'bg-green-500': status === 'correct',
"bg-yellow-500": status === "present", 'bg-yellow-500': status === 'present',
} }
); )
return ( return (
<> <>
<div className={classes}></div> <div className={classes}></div>
</> </>
); )
}; }

View file

@ -1,18 +1,18 @@
import { getGuessStatuses } from "../../lib/statuses"; import { getGuessStatuses } from '../../lib/statuses'
import { MiniCell } from "./MiniCell"; import { MiniCell } from './MiniCell'
type Props = { type Props = {
guess: string; guess: string
}; }
export const MiniCompletedRow = ({ guess }: Props) => { export const MiniCompletedRow = ({ guess }: Props) => {
const statuses = getGuessStatuses(guess); const statuses = getGuessStatuses(guess)
return ( return (
<div className="flex justify-center mb-1"> <div className="flex justify-center mb-1">
{guess.split("").map((letter, i) => ( {guess.split('').map((letter, i) => (
<MiniCell key={i} status={statuses[i]} /> <MiniCell key={i} status={statuses[i]} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,8 +1,8 @@
import { MiniCompletedRow } from "./MiniCompletedRow"; import { MiniCompletedRow } from './MiniCompletedRow'
type Props = { type Props = {
guesses: string[]; guesses: string[]
}; }
export const MiniGrid = ({ guesses }: Props) => { export const MiniGrid = ({ guesses }: Props) => {
return ( return (
@ -11,5 +11,5 @@ export const MiniGrid = ({ guesses }: Props) => {
<MiniCompletedRow key={i} guess={guess} /> <MiniCompletedRow key={i} guess={guess} />
))} ))}
</div> </div>
); )
}; }

View file

@ -1,10 +1,10 @@
import { Fragment } from "react"; import { Fragment } from 'react'
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from '@headlessui/react'
type Props = { type Props = {
isOpen: boolean; isOpen: boolean
handleClose: () => void; handleClose: () => void
}; }
export const AboutModal = ({ isOpen, handleClose }: Props) => { export const AboutModal = ({ isOpen, handleClose }: Props) => {
return ( return (
@ -54,14 +54,14 @@ export const AboutModal = ({ isOpen, handleClose }: Props) => {
</Dialog.Title> </Dialog.Title>
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
This is an open source clone of the game Wordle -{" "} This is an open source clone of the game Wordle -{' '}
<a <a
href="https://github.com/hannahcode/wordle" href="https://github.com/hannahcode/wordle"
className="underline font-bold" className="underline font-bold"
> >
check out the code here check out the code here
</a>{" "} </a>{' '}
and{" "} and{' '}
<a <a
href="https://www.powerlanguage.co.uk/wordle/" href="https://www.powerlanguage.co.uk/wordle/"
className="underline font-bold" className="underline font-bold"
@ -77,5 +77,5 @@ export const AboutModal = ({ isOpen, handleClose }: Props) => {
</div> </div>
</Dialog> </Dialog>
</Transition.Root> </Transition.Root>
); )
}; }

View file

@ -1,11 +1,11 @@
import { Fragment } from "react"; import { Fragment } from 'react'
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from '@headlessui/react'
import { Cell } from "../grid/Cell"; import { Cell } from '../grid/Cell'
type Props = { type Props = {
isOpen: boolean; isOpen: boolean
handleClose: () => void; handleClose: () => void
}; }
export const InfoModal = ({ isOpen, handleClose }: Props) => { export const InfoModal = ({ isOpen, handleClose }: Props) => {
return ( return (
@ -100,5 +100,5 @@ export const InfoModal = ({ isOpen, handleClose }: Props) => {
</div> </div>
</Dialog> </Dialog>
</Transition.Root> </Transition.Root>
); )
}; }

View file

@ -1,15 +1,15 @@
import { Fragment } from "react"; import { Fragment } from 'react'
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from '@headlessui/react'
import { CheckIcon } from "@heroicons/react/outline"; import { CheckIcon } from '@heroicons/react/outline'
import { MiniGrid } from "../mini-grid/MiniGrid"; import { MiniGrid } from '../mini-grid/MiniGrid'
import { shareStatus } from "../../lib/share"; import { shareStatus } from '../../lib/share'
type Props = { type Props = {
isOpen: boolean; isOpen: boolean
handleClose: () => void; handleClose: () => void
guesses: string[]; guesses: string[]
handleShare: () => void; handleShare: () => void
}; }
export const WinModal = ({ export const WinModal = ({
isOpen, isOpen,
@ -79,8 +79,8 @@ export const WinModal = ({
type="button" type="button"
className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm" className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:text-sm"
onClick={() => { onClick={() => {
shareStatus(guesses); shareStatus(guesses)
handleShare(); handleShare()
}} }}
> >
Share Share
@ -91,5 +91,5 @@ export const WinModal = ({
</div> </div>
</Dialog> </Dialog>
</Transition.Root> </Transition.Root>
); )
}; }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,17 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import './index.css'; import './index.css'
import App from './App'; import App from './App'
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals'
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
); )
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log)) // to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(); reportWebVitals()

View file

@ -1,3 +1,3 @@
import { CharValue } from "./statuses"; import { CharValue } from './statuses'
export type KeyValue = CharValue | "ENTER" | "DELETE"; export type KeyValue = CharValue | 'ENTER' | 'DELETE'

View file

@ -1,16 +1,16 @@
const gameStateKey = "gameState"; const gameStateKey = 'gameState'
type StoredGameState = { type StoredGameState = {
guesses: string[]; guesses: string[]
solution: string; solution: string
}; }
export const saveGameStateToLocalStorage = (gameState: StoredGameState) => { export const saveGameStateToLocalStorage = (gameState: StoredGameState) => {
localStorage.setItem(gameStateKey, JSON.stringify(gameState)); localStorage.setItem(gameStateKey, JSON.stringify(gameState))
}; }
export const loadGameStateFromLocalStorage = () => { export const loadGameStateFromLocalStorage = () => {
const state = localStorage.getItem(gameStateKey); const state = localStorage.getItem(gameStateKey)
return state ? (JSON.parse(state) as StoredGameState) : null; return state ? (JSON.parse(state) as StoredGameState) : null
}; }

View file

@ -1,34 +1,34 @@
import { getGuessStatuses } from "./statuses"; import { getGuessStatuses } from './statuses'
import { solutionIndex } from "./words"; import { solutionIndex } from './words'
export const shareStatus = (guesses: string[]) => { export const shareStatus = (guesses: string[]) => {
navigator.clipboard.writeText( navigator.clipboard.writeText(
"Wordle " + 'Wordle ' +
solutionIndex + solutionIndex +
" " + ' ' +
guesses.length + guesses.length +
"/6\n\n" + '/6\n\n' +
generateEmojiGrid(guesses) generateEmojiGrid(guesses)
); )
}; }
export const generateEmojiGrid = (guesses: string[]) => { export const generateEmojiGrid = (guesses: string[]) => {
return guesses return guesses
.map((guess) => { .map((guess) => {
const status = getGuessStatuses(guess); const status = getGuessStatuses(guess)
return guess return guess
.split("") .split('')
.map((letter, i) => { .map((letter, i) => {
switch (status[i]) { switch (status[i]) {
case "correct": case 'correct':
return "🟩"; return '🟩'
case "present": case 'present':
return "🟨"; return '🟨'
default: default:
return "⬜"; return '⬜'
} }
}) })
.join(""); .join('')
}) })
.join("\n"); .join('\n')
}; }

View file

@ -1,102 +1,102 @@
import { solution } from "./words"; import { solution } from './words'
export type CharStatus = "absent" | "present" | "correct"; export type CharStatus = 'absent' | 'present' | 'correct'
export type CharValue = export type CharValue =
| "Q" | 'Q'
| "W" | 'W'
| "E" | 'E'
| "R" | 'R'
| "T" | 'T'
| "Y" | 'Y'
| "U" | 'U'
| "I" | 'I'
| "O" | 'O'
| "P" | 'P'
| "A" | 'A'
| "S" | 'S'
| "D" | 'D'
| "F" | 'F'
| "G" | 'G'
| "H" | 'H'
| "J" | 'J'
| "K" | 'K'
| "L" | 'L'
| "Z" | 'Z'
| "X" | 'X'
| "C" | 'C'
| "V" | 'V'
| "B" | 'B'
| "N" | 'N'
| "M"; | 'M'
export const getStatuses = ( export const getStatuses = (
guesses: string[] guesses: string[]
): { [key: string]: CharStatus } => { ): { [key: string]: CharStatus } => {
const charObj: { [key: string]: CharStatus } = {}; const charObj: { [key: string]: CharStatus } = {}
guesses.forEach((word) => { guesses.forEach((word) => {
word.split("").forEach((letter, i) => { word.split('').forEach((letter, i) => {
if (!solution.includes(letter)) { if (!solution.includes(letter)) {
// make status absent // make status absent
return (charObj[letter] = "absent"); return (charObj[letter] = 'absent')
} }
if (letter === solution[i]) { if (letter === solution[i]) {
//make status correct //make status correct
return (charObj[letter] = "correct"); return (charObj[letter] = 'correct')
} }
if (charObj[letter] !== "correct") { if (charObj[letter] !== 'correct') {
//make status present //make status present
return (charObj[letter] = "present"); return (charObj[letter] = 'present')
} }
}); })
}); })
return charObj; return charObj
}; }
export const getGuessStatuses = (guess: string): CharStatus[] => { export const getGuessStatuses = (guess: string): CharStatus[] => {
const splitSolution = solution.split(""); const splitSolution = solution.split('')
const splitGuess = guess.split(""); const splitGuess = guess.split('')
const solutionCharsTaken = splitSolution.map((_) => false); const solutionCharsTaken = splitSolution.map((_) => false)
const statuses: CharStatus[] = Array.from(Array(guess.length)); const statuses: CharStatus[] = Array.from(Array(guess.length))
// handle all correct cases first // handle all correct cases first
splitGuess.forEach((letter, i) => { splitGuess.forEach((letter, i) => {
if (letter === splitSolution[i]) { if (letter === splitSolution[i]) {
statuses[i] = "correct"; statuses[i] = 'correct'
solutionCharsTaken[i] = true; solutionCharsTaken[i] = true
return; return
} }
}); })
splitGuess.forEach((letter, i) => { splitGuess.forEach((letter, i) => {
if (statuses[i]) return; if (statuses[i]) return
if (!splitSolution.includes(letter)) { if (!splitSolution.includes(letter)) {
// handles the absent case // handles the absent case
statuses[i] = "absent"; statuses[i] = 'absent'
return; return
} }
// now we are left with "present"s // now we are left with "present"s
const indexOfPresentChar = splitSolution.findIndex( const indexOfPresentChar = splitSolution.findIndex(
(x, index) => x === letter && !solutionCharsTaken[index] (x, index) => x === letter && !solutionCharsTaken[index]
); )
if (indexOfPresentChar > -1) { if (indexOfPresentChar > -1) {
statuses[i] = "present"; statuses[i] = 'present'
solutionCharsTaken[indexOfPresentChar] = true; solutionCharsTaken[indexOfPresentChar] = true
return; return
} else { } else {
statuses[i] = "absent"; statuses[i] = 'absent'
return; return
} }
}); })
return statuses; return statuses
}; }

View file

@ -1,28 +1,28 @@
import { WORDS } from "../constants/wordlist"; import { WORDS } from '../constants/wordlist'
import { VALIDGUESSES } from "../constants/validGuesses"; import { VALIDGUESSES } from '../constants/validGuesses'
export const isWordInWordList = (word: string) => { export const isWordInWordList = (word: string) => {
return ( return (
WORDS.includes(word.toLowerCase()) || WORDS.includes(word.toLowerCase()) ||
VALIDGUESSES.includes(word.toLowerCase()) VALIDGUESSES.includes(word.toLowerCase())
); )
}; }
export const isWinningWord = (word: string) => { export const isWinningWord = (word: string) => {
return solution === word; return solution === word
}; }
export const getWordOfDay = () => { export const getWordOfDay = () => {
// January 1, 2022 Game Epoch // January 1, 2022 Game Epoch
const epochMs = 1641013200000; const epochMs = 1641013200000
const now = Date.now(); const now = Date.now()
const msInDay = 86400000; const msInDay = 86400000
const index = Math.floor((now - epochMs) / msInDay); const index = Math.floor((now - epochMs) / msInDay)
return { return {
solution: WORDS[index].toUpperCase(), solution: WORDS[index].toUpperCase(),
solutionIndex: index, solutionIndex: index,
}; }
}; }
export const { solution, solutionIndex } = getWordOfDay(); export const { solution, solutionIndex } = getWordOfDay()

View file

@ -1,15 +1,15 @@
import { ReportHandler } from 'web-vitals'; import { ReportHandler } from 'web-vitals'
const reportWebVitals = (onPerfEntry?: ReportHandler) => { const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) { if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry); getCLS(onPerfEntry)
getFID(onPerfEntry); getFID(onPerfEntry)
getFCP(onPerfEntry); getFCP(onPerfEntry)
getLCP(onPerfEntry); getLCP(onPerfEntry)
getTTFB(onPerfEntry); getTTFB(onPerfEntry)
}); })
}
} }
};
export default reportWebVitals; export default reportWebVitals

View file

@ -2,4 +2,4 @@
// allows you to do things like: // allows you to do things like:
// expect(element).toHaveTextContent(/react/i) // expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom // learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom'; import '@testing-library/jest-dom'

View file

@ -1,7 +1,7 @@
module.exports = { module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"], content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: { theme: {
extend: {}, extend: {},
}, },
plugins: [], plugins: [],
}; }

View file

@ -1,11 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
@ -20,7 +16,5 @@
"noEmit": true, "noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx"
}, },
"include": [ "include": ["src"]
"src"
]
} }