diff --git a/package.json b/package.json index f131107..b119649 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/pashto-inflector", - "version": "1.9.9", + "version": "2.0.0", "author": "lingdocs.com", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "homepage": "https://verbs.lingdocs.com", diff --git a/src/components/Keyframes.tsx b/src/components/Keyframes.tsx new file mode 100644 index 0000000..82dca98 --- /dev/null +++ b/src/components/Keyframes.tsx @@ -0,0 +1,35 @@ +import { CSSProperties } from "react"; + +interface IProps { + name: string; + [key: string]: CSSProperties | string; +} + +const Keyframes = (props: IProps) => { + const toCss = (cssObject: CSSProperties | string) => + typeof cssObject === "string" + ? cssObject + : Object.keys(cssObject).reduce((accumulator, key) => { + const cssKey = key.replace(/[A-Z]/g, v => `-${v.toLowerCase()}`); + const cssValue = (cssObject as any)[key].toString().replace("'", ""); + return `${accumulator}${cssKey}:${cssValue};`; + }, ""); + + return ( + + ); +}; + +export default Keyframes; \ No newline at end of file diff --git a/src/components/vp-explorer/VPExplorerQuiz.tsx b/src/components/vp-explorer/VPExplorerQuiz.tsx index f4429ed..c965619 100644 --- a/src/components/vp-explorer/VPExplorerQuiz.tsx +++ b/src/components/vp-explorer/VPExplorerQuiz.tsx @@ -10,6 +10,8 @@ import { getRandomTense } from "./TensePicker"; import { switchSubjObj } from "../../lib/phrase-building/vp-tools"; import playAudio from "../../lib/play-audio"; import TensePicker from "./TensePicker"; +import Keyframes from "../Keyframes"; +import energyDrink from "./energy-drink.jpeg"; const correctEmoji = ["✅", '🤓', "✅", '😊', "🌹", "✅", "✅", "🕺", "💃", '🥳', "👏", "✅", "💯", "😎", "✅", "👍"]; @@ -25,8 +27,12 @@ const answerFeedback: CSSProperties = { } const checkDuration = 400; +const stageLength = 7; type QuizState = { + stage: "multiple choice", + qNumber: number, + vps: T.VPSelectionComplete, answer: { ps: T.SingleOrLengthOpts; e?: string[] | undefined; @@ -40,16 +46,10 @@ function VPExplorerQuiz(props: { opts: T.TextOptions, vps: T.VPSelection, }) { - const startingQs = makeQuizState(props.vps); - const [vps, setVps] = useState(startingQs.VPS) - const [quizState, setQuizState] = useState(startingQs.qs); + const startingQs = tickQuizState(props.vps); + const [quizState, setQuizState] = useState(startingQs); const [showCheck, setShowCheck] = useState(false); const [currentCorrectEmoji, setCurrentCorrectEmoji] = useState(randFromArray(correctEmoji)); - function handleResetQuiz() { - const { VPS, qs } = makeQuizState(vps); - setVps(VPS); - setQuizState(qs); - } function checkQuizAnswer(a: T.PsString) { if (!quizState) return; if (isInAnswer(a, quizState.answer)) { @@ -57,7 +57,7 @@ function VPExplorerQuiz(props: { if (toPlay) playAudio(`correct-${randFromArray([1,2,3])}`); setShowCheck(true); setTimeout(() => { - handleResetQuiz(); + setQuizState(tickQuizState); }, checkDuration / 2); setTimeout(() => { setShowCheck(false); @@ -75,11 +75,15 @@ function VPExplorerQuiz(props: { }); } } - const rendered = renderVP(vps); + const rendered = renderVP(quizState.vps); const { subject, object } = rendered; const { e } = compileVP(rendered, { removeKing: false, shrinkServant: false }); - return - + function handleRestart() { + setQuizState(tickQuizState(quizState.vps)); + } + return + + Subject {subject} @@ -90,44 +94,86 @@ function VPExplorerQuiz(props: { } null} mode={"quiz"} /> - {e && + {e && {e.map(eLine => {eLine})} } {currentCorrectEmoji} - {quizState.result === "waiting" ? <> - Choose a correct answer: - {quizState.options.map(o => - { - checkQuizAnswer(o); - }}> - {o} + {quizState.qNumber === stageLength ? + + 👏 Congratulations + You finished the first level! + The other levels are still in development... In the meantime have an energy drink. + + - )} - > : - ❌ Wrong 😭 - The correct answer was: - - {quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString} - - - - Try Again + + Restart - } + : (quizState.result === "waiting" + ? <> + Choose a correct answer: + {quizState.options.map(o => + { + checkQuizAnswer(o); + }}> + {o} + + )} + > + : + ❌ Wrong 😭 + The correct answer was: + + {quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString} + + + + Try Again + + + ) + } + ; } +function ProgressBar({ quizState }: { quizState: QuizState }) { + function getProgressWidth(): string { + const num = getPercentageDone({ current: quizState.qNumber, total: stageLength }); + return `${num}%`; + } + return + + + + + + Level 1: Multiple Choice + + ; +} + +function getPercentageDone(progress: { current: number, total: number }): number { + return Math.round( + (progress.current / (progress.total + 1)) * 100 + ); +} + function QuizNPDisplay({ children }: { children: T.Rendered | T.Person.ThirdPlurMale }) { return {(typeof children === "number") @@ -136,14 +182,21 @@ function QuizNPDisplay({ children }: { children: T.Rendered | T.P ; } - -function makeQuizState(oldVps: T.VPSelection): { VPS: T.VPSelectionComplete, qs: QuizState } { +/** + * creates a fresh QuizState when a VPSelection is passed + * advances a QuizState when a QuizState is passed + * + * @param startingWith + * @returns + */ +function tickQuizState(startingWith: T.VPSelection | QuizState): QuizState { function makeRes(x: T.VPSelectionComplete) { return compileVP(renderVP(x), { removeKing: false, shrinkServant: false }); } + const oldVps = "stage" in startingWith ? startingWith.vps : startingWith; // for now, always inforce positive - const vps = getRandomVPSelection("both")({ ...oldVps, verb: { ...oldVps.verb, negative: false }}); - const wrongStates: T.VPSelectionComplete[] = []; + const newVps = getRandomVPSelection("both")({ ...oldVps, verb: { ...oldVps.verb, negative: false }}); + const wrongVpsS: T.VPSelectionComplete[] = []; // don't do the SO switches every time const wholeTimeSOSwitch = randFromArray([true, false]); [1, 2, 3].forEach(() => { @@ -152,23 +205,24 @@ function makeQuizState(oldVps: T.VPSelection): { VPS: T.VPSelectionComplete, qs: const SOSwitch = wholeTimeSOSwitch && randFromArray([true, false]); // TODO: if switich subj and obj, include the tense being correct maybe v = getRandomVPSelection("tenses")( - SOSwitch ? switchSubjObj(vps) : vps, + SOSwitch ? switchSubjObj(newVps) : newVps, ); // eslint-disable-next-line - } while (wrongStates.find(x => x.verb.tense === v.verb.tense)); - wrongStates.push(v); + } while (wrongVpsS.find(x => x.verb.tense === v.verb.tense)); + wrongVpsS.push(v); }); - const answer = makeRes(vps); - const wrongAnswers = wrongStates.map(makeRes); + const answer = makeRes(newVps); + const wrongAnswers = wrongVpsS.map(makeRes); const allAnswers = shuffleArray([...wrongAnswers, answer]); const options = allAnswers.map(getOptionFromResult); + const qNumber = ("stage" in startingWith) ? (startingWith.qNumber + 1) : 0; return { - VPS: vps, - qs: { - answer, - options, - result: "waiting", - }, + stage: "multiple choice", + qNumber, + vps: newVps, + answer, + options, + result: "waiting", }; } @@ -224,8 +278,14 @@ function getRandomVPSelection(mix: MixType = "both") { distance: "far", person: obj, }; - const s = randSubj; // ensure that the verb selection is complete + if (mix === "tenses") { + return { + subject: subject !== undefined ? subject : randSubj, + // @ts-ignore + verb: randomizeTense(verb, true), + } + } const v: T.VerbSelectionComplete = { ...verb, object: ( @@ -236,14 +296,8 @@ function getRandomVPSelection(mix: MixType = "both") { ? randObj : verb.object, }; - if (mix === "tenses") { - return { - subject: subject !== undefined ? subject : randSubj, - verb: randomizeTense(v, true), - } - } return { - subject: s, + subject: randSubj, verb: randomizeTense(v, true), }; }; diff --git a/src/components/vp-explorer/energy-drink.jpeg b/src/components/vp-explorer/energy-drink.jpeg new file mode 100644 index 0000000..36df01e Binary files /dev/null and b/src/components/vp-explorer/energy-drink.jpeg differ
You finished the first level!
The other levels are still in development... In the meantime have an energy drink.