From 40b4a963da67182d1ddc489f57dea627ec5fc455 Mon Sep 17 00:00:00 2001 From: lingdocs <71590811+lingdocs@users.noreply.github.com> Date: Sat, 9 Apr 2022 22:31:55 +0500 Subject: [PATCH] added verb basic quiz feature --- package.json | 2 +- src/App.tsx | 1 + src/components/np-picker/NPPicker.tsx | 5 +- src/components/vp-explorer/TensePicker.tsx | 2 +- src/components/vp-explorer/VPDisplay.tsx | 6 +- src/components/vp-explorer/VPExplorer.tsx | 94 ++++++++++++++++++++-- src/lib/dictionary-models.js | 1 + src/lib/np-tools.ts | 2 +- src/lib/useStickyState.ts | 11 +-- 9 files changed, 102 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index c416714..a2e5ec4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lingdocs/pashto-inflector", - "version": "1.8.4", + "version": "1.8.5", "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/App.tsx b/src/App.tsx index 3646c66..1acd745 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,6 +33,7 @@ const verbTypes: VerbType[] = [ "stative compound", "dynamic compound", ]; + const nouns = nounsAdjs.filter(isNounEntry); const transitivities: T.Transitivity[] = [ diff --git a/src/components/np-picker/NPPicker.tsx b/src/components/np-picker/NPPicker.tsx index c0c999d..9e5b439 100644 --- a/src/components/np-picker/NPPicker.tsx +++ b/src/components/np-picker/NPPicker.tsx @@ -18,6 +18,7 @@ function NPPicker(props: { counterPart: T.NPSelection | T.VerbObject | undefined, asObject?: boolean, opts: T.TextOptions, + cantClear?: boolean, } & ({ nouns: (s: string) => T.NounEntry[], verbs: (s: string) => T.VerbEntry[], @@ -52,7 +53,9 @@ function NPPicker(props: { } } const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement; - const clearButton = ; + const clearButton = !props.cantClear + ? + :
; return
{!npType &&
{/*
diff --git a/src/components/vp-explorer/TensePicker.tsx b/src/components/vp-explorer/TensePicker.tsx index ff1f17a..efb4ab6 100644 --- a/src/components/vp-explorer/TensePicker.tsx +++ b/src/components/vp-explorer/TensePicker.tsx @@ -58,7 +58,7 @@ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense function TensePicker({ onChange, verb, mode }: { verb: T.VerbSelection | undefined, onChange: (p: T.VerbSelection | undefined) => void, - mode: "charts" | "phrases", + mode: "charts" | "phrases" | "quiz", }) { function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) { const value = o?.value ? o.value : undefined; diff --git a/src/components/vp-explorer/VPDisplay.tsx b/src/components/vp-explorer/VPDisplay.tsx index 85d2954..90f97bb 100644 --- a/src/components/vp-explorer/VPDisplay.tsx +++ b/src/components/vp-explorer/VPDisplay.tsx @@ -1,13 +1,13 @@ -import { useState } from "react"; import { renderVP, compileVP } from "../../lib/phrase-building/index"; import * as T from "../../types"; import InlinePs from "../InlinePs"; import AbbreviationFormSelector from "./AbbreviationFormSelector"; import { isPastTense } from "../../lib/phrase-building/vp-tools"; +import { useStickyState } from "../../library"; function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) { - const [form, setForm] = useState({ removeKing: false, shrinkServant: false }); - const [OSV, setOSV] = useState(false); + const [form, setForm] = useStickyState({ removeKing: false, shrinkServant: false }, "abbreviationForm"); + const [OSV, setOSV] = useStickyState(false, "includeOSV"); const result = compileVP(renderVP(VP), { ...form, OSV }); return
{VP.verb.transitivity === "transitive" &&
diff --git a/src/components/vp-explorer/VPExplorer.tsx b/src/components/vp-explorer/VPExplorer.tsx index 5555144..7e015a7 100644 --- a/src/components/vp-explorer/VPExplorer.tsx +++ b/src/components/vp-explorer/VPExplorer.tsx @@ -11,7 +11,8 @@ import * as T from "../../types"; import ChartDisplay from "./ChartDisplay"; import useStickyState from "../../lib/useStickyState"; import { makeVerbSelection } from "./verb-selection"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; +import { randomSubjObj } from "../../library"; const kingEmoji = "👑"; const servantEmoji = "🙇‍♂️"; @@ -41,7 +42,8 @@ export function VPExplorer(props: { getVerbByTs: (ts: number) => T.VerbEntry | undefined, })) { const [subject, setSubject] = useStickyState(undefined, "subjectNPSelection"); - const [mode, setMode] = useStickyState<"charts" | "phrases">("phrases", "verbExplorerMode"); + const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode"); + const [showAnswer, setShowAnswer] = useState(false); const passedVerb = props.verb; const [verb, setVerb] = useStickyState( passedVerb @@ -57,6 +59,28 @@ export function VPExplorer(props: { } // eslint-disable-next-line }, [passedVerb]); + function handleChangeMode(m: "charts" | "phrases" | "quiz") { + if (m === "quiz") { + handleResetQuiz(); + } + setMode(m); + } + function handleSetVerb(v: T.VerbSelection | undefined) { + if (v?.verb.entry.ts !== verb?.verb.entry.ts) { + handleResetQuiz(); + } + setVerb(v); + } + function handleResetQuiz() { + if (!verb) { + alert("Choose a verb to quiz"); + return; + } + const { S, V } = setRandomQuizState(subject, verb); + setShowAnswer(false); + setSubject(S); + setVerb(V); + } function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) { if (!skipPronounConflictCheck && hasPronounConflict(subject, verb?.object)) { alert("That combination of pronouns is not allowed"); @@ -94,17 +118,18 @@ export function VPExplorer(props: { verb={verb} subject={subject} changeSubject={(s) => handleSubjectChange(s, true)} - onChange={setVerb} + onChange={handleSetVerb} opts={props.opts} /> -
+
{(verb && (typeof verb.object === "object") && (verb.isCompound !== "dynamic") && (mode !== "charts")) && @@ -131,6 +156,7 @@ export function VPExplorer(props: { counterPart={verb ? verb.object : undefined} onChange={handleSubjectChange} opts={props.opts} + cantClear={mode === "quiz"} />
{verb && (verb.object !== "none") &&
@@ -152,21 +178,32 @@ export function VPExplorer(props: { counterPart={subject} onChange={handleObjectChange} opts={props.opts} + cantClear={mode === "quiz"} />}
} }
- {(verbPhrase && (mode === "phrases")) && + {(verb && (mode === "quiz")) &&
+ +
} + {(verbPhrase && ((mode === "phrases") || (mode === "quiz" && showAnswer))) && } - {(verb && (mode === "charts")) && } + {(verb && (mode === "charts")) && }
} @@ -211,4 +248,45 @@ function switchSubjObj({ subject, verb }: SOClump): SOClump { object: subject, } }; +} + +function setRandomQuizState(subject: T.NPSelection | undefined, verb: T.VerbSelection): { + S: T.NPSelection, + V: T.VerbSelection, +} { + const oldSubj = (subject?.type === "pronoun") + ? subject.person + : undefined; + const oldObj = (typeof verb?.object === "object" && verb.object.type === "pronoun") + ? verb.object.person + : undefined; + const { subj, obj } = randomSubjObj( + oldSubj !== undefined ? { subj: oldSubj, obj: oldObj } : undefined + ); + const randSubj: T.PronounSelection = subject?.type === "pronoun" ? { + ...subject, + person: subj, + } : { + type: "pronoun", + distance: "far", + person: subj, + }; + const randObj: T.PronounSelection = typeof verb?.object === "object" && verb.object.type === "pronoun" ? { + ...verb.object, + person: obj, + } : { + type: "pronoun", + distance: "far", + person: obj, + }; + return { + // TODO: Randomize the near/far ?? + S: randSubj, + V: { + ...verb, + object: (typeof verb.object === "object" || verb.object === undefined) + ? randObj + : verb.object, + }, + } } \ No newline at end of file diff --git a/src/lib/dictionary-models.js b/src/lib/dictionary-models.js index 5801f46..cec16a9 100644 --- a/src/lib/dictionary-models.js +++ b/src/lib/dictionary-models.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line 'use strict'; // code generated by pbf v3.2.1 // DictionaryInfo ======================================== diff --git a/src/lib/np-tools.ts b/src/lib/np-tools.ts index 94fc803..57ed9e4 100644 --- a/src/lib/np-tools.ts +++ b/src/lib/np-tools.ts @@ -50,7 +50,7 @@ export function isInvalidSubjObjCombo(subj: T.Person, obj: T.Person): boolean { ); } -export function randomSubjObj(old?: { subj: T.Person, obj: T.Person }): { subj: T.Person, obj: T.Person } { +export function randomSubjObj(old?: { subj: T.Person, obj?: T.Person }): { subj: T.Person, obj: T.Person } { let subj = 0; let obj = 0; do { diff --git a/src/lib/useStickyState.ts b/src/lib/useStickyState.ts index ce824e3..6e86a04 100644 --- a/src/lib/useStickyState.ts +++ b/src/lib/useStickyState.ts @@ -8,12 +8,9 @@ import { useEffect, useState } from "react"; * @param key a key for saving the state in locolStorage * @returns */ -export default function useStickyState( - defaultValue: T | ((old: T | undefined) => T), - key: string -): [ - value: T, - setValue: React.Dispatch>, +export default function useStickyState(defaultValue: T | ((old: T | undefined) => T), key: string): [ + value: T, + setValue: React.Dispatch>, ] { const [value, setValue] = useState(() => { const v = window.localStorage.getItem(key); @@ -30,7 +27,7 @@ export default function useStickyState