added verb basic quiz feature
This commit is contained in:
parent
bfdc95f774
commit
40b4a963da
|
@ -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",
|
||||||
|
|
|
@ -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[] = [
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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,21 +178,32 @@ 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} />}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,4 +248,45 @@ function switchSubjObj({ subject, verb }: SOClump): SOClump {
|
||||||
object: subject,
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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 ========================================
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue