From 3f8452525ff6c94bae184c5da964ab1fce5d46d3 Mon Sep 17 00:00:00 2001 From: Akshat Date: Fri, 31 Oct 2025 16:36:49 +0530 Subject: [PATCH] task done --- src/data/content.js | 9 ++ src/pages/games/TypingTest.js | 166 +++++++++++++++++++++++++ src/styles/pages/games/TypingTest.css | 169 ++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 src/pages/games/TypingTest.js create mode 100644 src/styles/pages/games/TypingTest.css diff --git a/src/data/content.js b/src/data/content.js index 1652e19..a9e379d 100644 --- a/src/data/content.js +++ b/src/data/content.js @@ -28,6 +28,7 @@ import { CatHttpCode } from "../pages/activities/CatHttpCode"; import FlappyBird from "../pages/games/FlappyBird"; import RandomIdentity from "../pages/activities/RandomIdentity"; import word_scramble_icon from "../assets/games/WordScramble/word_scramble.png"; +import TypingTest from "../pages/games/TypingTest"; export const activities = [ { @@ -179,5 +180,13 @@ export const games = [ icon: word_scramble_icon, urlTerm: "WordScramble", element: , + }, + { + title: "Typing Test", + description: "Test your typing skills", + icon :"https://typingmentor.com/_next/image/?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fbs3wcomf%2Fproduction%2F9f626374aa1c388a6418a077990771aff53d054f-1200x800.jpg%3Frect%3D0%2C35%2C1200%2C731%26w%3D808%26h%3D492%26fit%3Dcrop%26auto%3Dformat&w=1920&q=75", + urlTerm:"TypingTest", + element:, + } ]; \ No newline at end of file diff --git a/src/pages/games/TypingTest.js b/src/pages/games/TypingTest.js new file mode 100644 index 0000000..009392c --- /dev/null +++ b/src/pages/games/TypingTest.js @@ -0,0 +1,166 @@ +import React, { useEffect, useMemo, useState } from "react"; +import "../../styles/pages/games/TypingTest.css"; + +const SAMPLE_PARAGRAPHS = [ + "The quick brown fox jumps over the lazy dog.", + "Typing is a skill that improves with focus and consistency.", + "React makes it painless to create interactive user interfaces.", + "Code slowly, read carefully, and test your changes.", + "Practice daily to increase your speed and accuracy." +]; + +function getRandomParagraph() { + const idx = Math.floor(Math.random() * SAMPLE_PARAGRAPHS.length); + return SAMPLE_PARAGRAPHS[idx]; +} + +export default function TypingTest() { + const [targetText, setTargetText] = useState(getRandomParagraph); + const [typedText, setTypedText] = useState(""); + const [started, setStarted] = useState(false); + const [time, setTime] = useState(0); + const [finished, setFinished] = useState(false); + const [bestWpm, setBestWpm] = useState(() => { + const fromLS = localStorage.getItem("typing_best_wpm"); + return fromLS ? Number(fromLS) : 0; + }); + + // timer: start on first key, stop on finish + useEffect(() => { + let timer; + if (started && !finished) { + timer = setInterval(() => { + setTime((t) => t + 1); + }, 1000); + } else { + if (timer) clearInterval(timer); + } + return () => { + if (timer) clearInterval(timer); + }; + }, [started, finished]); + + // derived metrics + const stats = useMemo(() => { + const wordsTyped = + typedText.trim().length === 0 + ? 0 + : typedText.trim().split(/\s+/).length; + const minutes = time / 60; + const wpm = minutes > 0 ? Math.round(wordsTyped / minutes) : 0; + + // accuracy + let correct = 0; + for (let i = 0; i < typedText.length; i++) { + if (typedText[i] === targetText[i]) correct++; + } + const accuracy = + typedText.length === 0 + ? 100 + : Math.round((correct / typedText.length) * 100); + + return { wpm, accuracy, wordsTyped }; + }, [typedText, time, targetText]); + + // when completed, stop and save best + useEffect(() => { + if (typedText === targetText && targetText.length > 0) { + setFinished(true); + setStarted(false); + if (stats.wpm > bestWpm) { + setBestWpm(stats.wpm); + localStorage.setItem("typing_best_wpm", String(stats.wpm)); + } + } + }, [typedText, targetText, stats.wpm, bestWpm]); + + function handleChange(e) { + const val = e.target.value; + + // auto start on first key + if (!started && !finished) { + setStarted(true); + } + + // restrict to paragraph length + if (val.length <= targetText.length) { + setTypedText(val); + + // auto end on completion + if (val === targetText) { + setFinished(true); + setStarted(false); + } + } + } + + function handleRestart() { + const nextPara = getRandomParagraph(); + setTargetText(nextPara); + setTypedText(""); + setTime(0); + setFinished(false); + setStarted(false); + } + + return ( +
+
+

Typing Speed Test

+

Type the text below as fast and as accurately as you can.

+
+ +
+
+ Time + {time}s +
+
+ WPM + {stats.wpm} +
+
+ Accuracy + {stats.accuracy}% +
+
+ Best WPM + {bestWpm} +
+
+ +
+ {targetText.split("").map((ch, idx) => { + let cls = ""; + if (idx < typedText.length) { + cls = typedText[idx] === ch ? "correct" : "incorrect"; + } + return ( + + {ch} + + ); + })} +
+ +