From abd723efc6bb78b3995fa535f7d4248211d1d2af Mon Sep 17 00:00:00 2001 From: JohnHKoh Date: Sun, 23 Jan 2022 20:25:32 -0500 Subject: [PATCH 1/2] Add word definition link on hover and on game end --- src/App.css | 43 ++++++++++++++++++++++++++++++++++++++ src/Game.tsx | 29 +++++++++++++++++++------- src/Row.tsx | 48 ++++++++++++++++++++++++++++++++++++++++++- src/clue.ts | 8 ++++++++ src/declaration.d.ts | 1 + src/images/search.svg | 1 + 6 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/declaration.d.ts create mode 100644 src/images/search.svg diff --git a/src/App.css b/src/App.css index bae7511d6..843b9f76d 100644 --- a/src/App.css +++ b/src/App.css @@ -25,6 +25,13 @@ body { align-items: center; text-transform: uppercase; font-weight: bold; + position: relative; +} + +.Row-letter:before { + content: ''; + position: absolute; + inset: -4px -4px -4px -4px; } .Row-annotation { @@ -33,6 +40,42 @@ body { text-align: start; } +.Row-definition { + position: absolute; + padding-left: 12px; + height: 40px; + margin-left: 3px; + display: flex; + align-items: center; + justify-content: center; +} + +.Icon-container { + display: flex; + align-items: center; + justify-content: center; +} + +.Icon-container img { + height: 18px; + width: 18px; + filter: invert(61%) sepia(85%) saturate(3%) hue-rotate(318deg) brightness(93%) contrast(82%); +} + +.Icon-container img:hover { + filter: invert(50%) sepia(0%) saturate(15%) hue-rotate(137deg) brightness(98%) contrast(89%); +} + +.target-word { + color: unset !important; + display: inline; + text-decoration: none; +} + +.target-word:hover { + text-decoration: underline; +} + .App-container { position: relative; max-width: 500px; diff --git a/src/Game.tsx b/src/Game.tsx index eb5051d7c..d2916cb72 100644 --- a/src/Game.tsx +++ b/src/Game.tsx @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from "react"; import { Row, RowState } from "./Row"; import dictionary from "./dictionary.json"; -import { Clue, clue, describeClue, violation } from "./clue"; +import { Clue, clue, describeClue, getClueDefinitionLink, getStringDefinitionLink, violation } from "./clue"; import { Keyboard } from "./Keyboard"; import targetList from "./targets.json"; import { @@ -66,7 +66,7 @@ function Game(props: GameProps) { const [gameState, setGameState] = useState(GameState.Playing); const [guesses, setGuesses] = useState([]); const [currentGuess, setCurrentGuess] = useState(""); - const [hint, setHint] = useState( + const [hint, setHint] = useState( challengeError ? `Invalid challenge string, playing random game.` : `Make your first guess!` @@ -136,9 +136,14 @@ function Game(props: GameProps) { setCurrentGuess((guess) => ""); const gameOver = (verbed: string) => - `You ${verbed}! The answer was ${target.toUpperCase()}. (Enter to ${ - challenge ? "play a random game" : "play again" - })`; + <> + You ${verbed}! The answer was  + + {target.toUpperCase()} + + . (Enter to { + challenge ? "play a random game" : "play again"} + ; if (currentGuess === target) { setHint(gameOver("won")); @@ -231,7 +236,17 @@ function Game(props: GameProps) { disabled={gameState !== GameState.Playing || guesses.length === 0} onClick={() => { setHint( - `The answer was ${target.toUpperCase()}. (Enter to play again)` + <> + The answer was  + + {target.toUpperCase()} + + . (Enter to play again) + ); setGameState(GameState.Lost); (document.activeElement as HTMLElement)?.blur(); @@ -250,7 +265,7 @@ function Game(props: GameProps) {

{hint || `\u00a0`}

diff --git a/src/Row.tsx b/src/Row.tsx index ecbdb8de6..b2fa83b49 100644 --- a/src/Row.tsx +++ b/src/Row.tsx @@ -1,4 +1,6 @@ -import { Clue, clueClass, CluedLetter, clueWord } from "./clue"; +import { MouseEvent, useRef, useState } from "react"; +import { Clue, clueClass, CluedLetter, clueWord, getClueDefinitionLink } from "./clue"; +import searchIcon from "./images/search.svg"; export enum RowState { LockedIn, @@ -13,14 +15,34 @@ interface RowProps { annotation?: string; } +interface DefinitionProps { + shown: boolean, + left: number; + width: number; +} + export function Row(props: RowProps) { + const [definitionInfo, setDefinitionInfo] = useState({ shown: false, left: 0, width: 0 }); + const lastLetterRef = useRef(null); const isLockedIn = props.rowState === RowState.LockedIn; const isEditing = props.rowState === RowState.Editing; + function showDictionary(e: MouseEvent) { + let lastLetterLeft = lastLetterRef.current?.offsetLeft ?? 0; + let lastLetterWidth = lastLetterRef.current?.offsetWidth ?? 0; + setDefinitionInfo({ shown: true, left: lastLetterLeft, width: lastLetterWidth }); + } + function hideDictionary(e: MouseEvent) { + let toElement = e.relatedTarget as Element; + if (toElement.className === "Row-definition" || toElement.className === "Icon-img") return; + setDefinitionInfo({ shown: false, left: 0, width: 0 }); + + } const letterDivs = props.cluedLetters .concat(Array(props.wordLength).fill({ clue: Clue.Absent, letter: "" })) .slice(0, props.wordLength) .map(({ clue, letter }, i) => { let letterClass = "Row-letter"; + let isLastLetter = i === props.wordLength - 1; if (isLockedIn && clue !== undefined) { letterClass += " " + clueClass(clue); } @@ -35,6 +57,9 @@ export function Row(props: RowProps) { (clue === undefined ? "" : ": " + clueWord(clue)) : "" } + onMouseEnter={showDictionary} + onMouseLeave={hideDictionary} + ref={isLastLetter ? lastLetterRef : undefined} > {letter} @@ -48,6 +73,27 @@ export function Row(props: RowProps) { {props.annotation && ( {props.annotation} )} + {isLockedIn && definitionInfo.shown && ( +
+ + + +
+ )} ); } diff --git a/src/clue.ts b/src/clue.ts index 3e9c43fb8..ebad037c3 100644 --- a/src/clue.ts +++ b/src/clue.ts @@ -109,3 +109,11 @@ export function violation( } return undefined; } + +export function getClueDefinitionLink(clues: CluedLetter[]): string { + return getStringDefinitionLink(clues.map(clue => clue.letter).join('')); +} + +export function getStringDefinitionLink(word: string): string { + return `https://www.collinsdictionary.com/dictionary/english/${word}`; +} diff --git a/src/declaration.d.ts b/src/declaration.d.ts new file mode 100644 index 000000000..692d88b3b --- /dev/null +++ b/src/declaration.d.ts @@ -0,0 +1 @@ +declare module "*.svg"; \ No newline at end of file diff --git a/src/images/search.svg b/src/images/search.svg new file mode 100644 index 000000000..bca2caf3f --- /dev/null +++ b/src/images/search.svg @@ -0,0 +1 @@ + \ No newline at end of file From ec6a1cc47296f4184517146ccdc44fd9ff888d9e Mon Sep 17 00:00:00 2001 From: JohnHKoh Date: Tue, 25 Jan 2022 00:18:37 -0500 Subject: [PATCH 2/2] Add missing comma to style --- src/Game.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Game.tsx b/src/Game.tsx index 18612eb74..ed644ff55 100644 --- a/src/Game.tsx +++ b/src/Game.tsx @@ -289,7 +289,7 @@ function Game(props: GameProps) {