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.

+
+ energy-dring
-
)} - :
-
❌ Wrong 😭
-
The correct answer was:
- - {quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString} - -
-
-
} + : (quizState.result === "waiting" + ? <> +
Choose a correct answer:
+ {quizState.options.map(o =>
+ +
)} + + :
+
❌ Wrong 😭
+
The correct answer was:
+ + {quizState.options.find(x => isInAnswer(x, quizState.answer)) as T.PsString} + +
+ +
+
) + } +
; } +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