added verb basic quiz feature

This commit is contained in:
lingdocs 2022-04-09 22:31:55 +05:00
parent bfdc95f774
commit 40b4a963da
9 changed files with 102 additions and 22 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@lingdocs/pashto-inflector", "name": "@lingdocs/pashto-inflector",
"version": "1.8.4", "version": "1.8.5",
"author": "lingdocs.com", "author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com", "homepage": "https://verbs.lingdocs.com",

View File

@ -33,6 +33,7 @@ const verbTypes: VerbType[] = [
"stative compound", "stative compound",
"dynamic compound", "dynamic compound",
]; ];
const nouns = nounsAdjs.filter(isNounEntry); const nouns = nounsAdjs.filter(isNounEntry);
const transitivities: T.Transitivity[] = [ const transitivities: T.Transitivity[] = [

View File

@ -18,6 +18,7 @@ function NPPicker(props: {
counterPart: T.NPSelection | T.VerbObject | undefined, counterPart: T.NPSelection | T.VerbObject | undefined,
asObject?: boolean, asObject?: boolean,
opts: T.TextOptions, opts: T.TextOptions,
cantClear?: boolean,
} & ({ } & ({
nouns: (s: string) => T.NounEntry[], nouns: (s: string) => T.NounEntry[],
verbs: (s: string) => T.VerbEntry[], verbs: (s: string) => T.VerbEntry[],
@ -52,7 +53,9 @@ function NPPicker(props: {
} }
} }
const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement; const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement;
const clearButton = <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>; const clearButton = !props.cantClear
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
: <div></div>;
return <div> return <div>
{!npType && <div className="text-center mt-3"> {!npType && <div className="text-center mt-3">
{/* <div className="h6 mr-3"> {/* <div className="h6 mr-3">

View File

@ -58,7 +58,7 @@ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense
function TensePicker({ onChange, verb, mode }: { function TensePicker({ onChange, verb, mode }: {
verb: T.VerbSelection | undefined, verb: T.VerbSelection | undefined,
onChange: (p: T.VerbSelection | undefined) => void, onChange: (p: T.VerbSelection | undefined) => void,
mode: "charts" | "phrases", mode: "charts" | "phrases" | "quiz",
}) { }) {
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) { function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) {
const value = o?.value ? o.value : undefined; const value = o?.value ? o.value : undefined;

View File

@ -1,13 +1,13 @@
import { useState } from "react";
import { renderVP, compileVP } from "../../lib/phrase-building/index"; import { renderVP, compileVP } from "../../lib/phrase-building/index";
import * as T from "../../types"; import * as T from "../../types";
import InlinePs from "../InlinePs"; import InlinePs from "../InlinePs";
import AbbreviationFormSelector from "./AbbreviationFormSelector"; import AbbreviationFormSelector from "./AbbreviationFormSelector";
import { isPastTense } from "../../lib/phrase-building/vp-tools"; import { isPastTense } from "../../lib/phrase-building/vp-tools";
import { useStickyState } from "../../library";
function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) { function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
const [form, setForm] = useState<T.FormVersion>({ removeKing: false, shrinkServant: false }); const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
const [OSV, setOSV] = useState<boolean>(false); const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
const result = compileVP(renderVP(VP), { ...form, OSV }); const result = compileVP(renderVP(VP), { ...form, OSV });
return <div className="text-center mt-2"> return <div className="text-center mt-2">
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2"> {VP.verb.transitivity === "transitive" && <div className="form-check mb-2">

View File

@ -11,7 +11,8 @@ import * as T from "../../types";
import ChartDisplay from "./ChartDisplay"; import ChartDisplay from "./ChartDisplay";
import useStickyState from "../../lib/useStickyState"; import useStickyState from "../../lib/useStickyState";
import { makeVerbSelection } from "./verb-selection"; import { makeVerbSelection } from "./verb-selection";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { randomSubjObj } from "../../library";
const kingEmoji = "👑"; const kingEmoji = "👑";
const servantEmoji = "🙇‍♂️"; const servantEmoji = "🙇‍♂️";
@ -41,7 +42,8 @@ export function VPExplorer(props: {
getVerbByTs: (ts: number) => T.VerbEntry | undefined, getVerbByTs: (ts: number) => T.VerbEntry | undefined,
})) { })) {
const [subject, setSubject] = useStickyState<T.NPSelection | undefined>(undefined, "subjectNPSelection"); const [subject, setSubject] = useStickyState<T.NPSelection | undefined>(undefined, "subjectNPSelection");
const [mode, setMode] = useStickyState<"charts" | "phrases">("phrases", "verbExplorerMode"); const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode");
const [showAnswer, setShowAnswer] = useState<boolean>(false);
const passedVerb = props.verb; const passedVerb = props.verb;
const [verb, setVerb] = useStickyState<T.VerbSelection | undefined>( const [verb, setVerb] = useStickyState<T.VerbSelection | undefined>(
passedVerb passedVerb
@ -57,6 +59,28 @@ export function VPExplorer(props: {
} }
// eslint-disable-next-line // eslint-disable-next-line
}, [passedVerb]); }, [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) { function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
if (!skipPronounConflictCheck && hasPronounConflict(subject, verb?.object)) { if (!skipPronounConflictCheck && hasPronounConflict(subject, verb?.object)) {
alert("That combination of pronouns is not allowed"); alert("That combination of pronouns is not allowed");
@ -94,17 +118,18 @@ export function VPExplorer(props: {
verb={verb} verb={verb}
subject={subject} subject={subject}
changeSubject={(s) => handleSubjectChange(s, true)} changeSubject={(s) => handleSubjectChange(s, true)}
onChange={setVerb} onChange={handleSetVerb}
opts={props.opts} opts={props.opts}
/> />
<div className="mt-2 mb-3"> <div className="mt-2 mb-3 text-center">
<ButtonSelect <ButtonSelect
value={mode} value={mode}
options={[ options={[
{ label: "Charts", value: "charts" }, { label: "Charts", value: "charts" },
{ label: "Phrases", value: "phrases" }, { label: "Phrases", value: "phrases" },
{ label: "Quiz", value: "quiz" },
]} ]}
handleChange={setMode} handleChange={handleChangeMode}
/> />
</div> </div>
{(verb && (typeof verb.object === "object") && (verb.isCompound !== "dynamic") && (mode !== "charts")) && {(verb && (typeof verb.object === "object") && (verb.isCompound !== "dynamic") && (mode !== "charts")) &&
@ -131,6 +156,7 @@ export function VPExplorer(props: {
counterPart={verb ? verb.object : undefined} counterPart={verb ? verb.object : undefined}
onChange={handleSubjectChange} onChange={handleSubjectChange}
opts={props.opts} opts={props.opts}
cantClear={mode === "quiz"}
/> />
</div> </div>
{verb && (verb.object !== "none") && <div className="my-2"> {verb && (verb.object !== "none") && <div className="my-2">
@ -152,18 +178,29 @@ export function VPExplorer(props: {
counterPart={subject} counterPart={subject}
onChange={handleObjectChange} onChange={handleObjectChange}
opts={props.opts} opts={props.opts}
cantClear={mode === "quiz"}
/>} />}
</div>} </div>}
</>} </>}
<div className="my-2"> <div className="my-2">
<TensePicker <TensePicker
verb={verb} verb={verb}
onChange={setVerb} onChange={handleSetVerb}
mode={mode} mode={mode}
/> />
</div> </div>
</div> </div>
{(verbPhrase && (mode === "phrases")) && {(verb && (mode === "quiz")) && <div className="text-center my-2">
<button
className="btn btn-primary"
onClick={showAnswer ? handleResetQuiz : () => setShowAnswer(true)}
>
{showAnswer
? <>Next <i className="ml-1 fas fa-random"/></>
: <>Show Answer</>}
</button>
</div>}
{(verbPhrase && ((mode === "phrases") || (mode === "quiz" && showAnswer))) &&
<VPDisplay VP={verbPhrase} opts={props.opts} /> <VPDisplay VP={verbPhrase} opts={props.opts} />
} }
{(verb && (mode === "charts")) && <ChartDisplay VS={verb} opts={props.opts} />} {(verb && (mode === "charts")) && <ChartDisplay VS={verb} opts={props.opts} />}
@ -212,3 +249,44 @@ function switchSubjObj({ subject, verb }: SOClump): SOClump {
} }
}; };
} }
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,
},
}
}

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line
'use strict'; // code generated by pbf v3.2.1 'use strict'; // code generated by pbf v3.2.1
// DictionaryInfo ======================================== // DictionaryInfo ========================================

View File

@ -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 subj = 0;
let obj = 0; let obj = 0;
do { do {

View File

@ -8,12 +8,9 @@ import { useEffect, useState } from "react";
* @param key a key for saving the state in locolStorage * @param key a key for saving the state in locolStorage
* @returns * @returns
*/ */
export default function useStickyState<T extends string | object | boolean | undefined | null>( export default function useStickyState<T extends string | object | boolean | undefined | null>(defaultValue: T | ((old: T | undefined) => T), key: string): [
defaultValue: T | ((old: T | undefined) => T), value: T,
key: string setValue: React.Dispatch<React.SetStateAction<T>>,
): [
value: T,
setValue: React.Dispatch<React.SetStateAction<T>>,
] { ] {
const [value, setValue] = useState<T>(() => { const [value, setValue] = useState<T>(() => {
const v = window.localStorage.getItem(key); const v = window.localStorage.getItem(key);
@ -30,7 +27,7 @@ export default function useStickyState<T extends string | object | boolean | und
if (typeof defaultValue === "function") { if (typeof defaultValue === "function") {
return defaultValue(old); return defaultValue(old);
} }
return defaultValue; return old;
} catch (e) { } catch (e) {
console.error("error parsting saved state from stickState"); console.error("error parsting saved state from stickState");
return (typeof defaultValue === "function") return (typeof defaultValue === "function")