proper quizzin
This commit is contained in:
parent
a2a0200a38
commit
3f58fb6bef
|
@ -87,6 +87,10 @@ function App() {
|
|||
if (transitivity === "grammatically transitive") {
|
||||
setVerbTypeShowing("simple");
|
||||
}
|
||||
if (transitivity === "intransitive" && verbTypeShowing === "dynamic compound") {
|
||||
setTransitivityShowing("transitive");
|
||||
return;
|
||||
}
|
||||
setTransitivityShowing(e.target.value as T.Transitivity);
|
||||
}
|
||||
const isRegularVerb = (entry: T.DictionaryEntry): boolean => (
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as T from "../../types";
|
|||
import ButtonSelect from "../ButtonSelect";
|
||||
import { isPerfectTense } from "../../lib/phrase-building/vp-tools";
|
||||
|
||||
const tenseOptions: { label: string | JSX.Element, value: T.VerbTense }[] = [{
|
||||
const verbTenseOptions: { label: string | JSX.Element, value: T.VerbTense }[] = [{
|
||||
label: <div><i className="fas fa-video mr-2" />present</div>,
|
||||
value: "presentVerb",
|
||||
}, {
|
||||
|
@ -55,34 +55,53 @@ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense
|
|||
value: "pastSubjunctive perfect",
|
||||
}];
|
||||
|
||||
function TensePicker({ onChange, verb, mode }: {
|
||||
verb: T.VerbSelection,
|
||||
onChange: (p: T.VerbSelection) => void,
|
||||
export function getRandomTense(type: "basic" | "modal", o?: T.VerbTense): T.VerbTense;
|
||||
export function getRandomTense(type: "perfect", o?: T.PerfectTense | T.VerbTense): T.PerfectTense;
|
||||
export function getRandomTense(type: "basic" | "modal" | "perfect", o?: T.PerfectTense | T.VerbTense): T.PerfectTense | T.VerbTense {
|
||||
let tns: T.PerfectTense | T.VerbTense;
|
||||
const tenseOptions = type === "perfect" ? perfectTenseOptions : verbTenseOptions;
|
||||
do {
|
||||
tns = tenseOptions[
|
||||
Math.floor(Math.random()*tenseOptions.length)
|
||||
].value;
|
||||
} while (o === tns);
|
||||
return tns;
|
||||
}
|
||||
|
||||
function TensePicker({ onChange, vps, mode }: {
|
||||
vps: T.VPSelectionState,
|
||||
onChange: (p: T.VPSelectionState) => void,
|
||||
mode: "charts" | "phrases" | "quiz",
|
||||
}) {
|
||||
function onTenseSelect(o: { value: T.VerbTense | T.PerfectTense } | null) {
|
||||
const value = o?.value ? o.value : undefined;
|
||||
if (verb && value) {
|
||||
if (vps.verb && value) {
|
||||
if (isPerfectTense(value)) {
|
||||
onChange({
|
||||
...verb,
|
||||
tense: value,
|
||||
tenseCategory: "perfect",
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
tense: value,
|
||||
tenseCategory: "perfect",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
onChange({
|
||||
...verb,
|
||||
tense: value,
|
||||
tenseCategory: verb.tenseCategory === "perfect" ? "basic" : verb.tenseCategory,
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
tense: value,
|
||||
tenseCategory: vps.verb.tenseCategory === "perfect" ? "basic" : vps.verb.tenseCategory,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function moveTense(dir: "forward" | "back") {
|
||||
if (!verb) return;
|
||||
if (!vps.verb) return;
|
||||
return () => {
|
||||
const tenses = verb.tenseCategory === "perfect" ? perfectTenseOptions : tenseOptions;
|
||||
const currIndex = tenses.findIndex(tn => tn.value === verb.tense)
|
||||
const tenses = vps.verb.tenseCategory === "perfect" ? perfectTenseOptions : verbTenseOptions;
|
||||
const currIndex = tenses.findIndex(tn => tn.value === vps.verb.tense)
|
||||
if (currIndex === -1) {
|
||||
console.error("error moving tense", dir);
|
||||
return;
|
||||
|
@ -95,39 +114,48 @@ function TensePicker({ onChange, verb, mode }: {
|
|||
};
|
||||
}
|
||||
function onPosNegSelect(value: string) {
|
||||
if (verb) {
|
||||
if (vps.verb) {
|
||||
onChange({
|
||||
...verb,
|
||||
negative: value === "true",
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
negative: value === "true",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
function onTenseCategorySelect(value: "basic" | "modal" | "perfect") {
|
||||
if (verb) {
|
||||
if (vps.verb) {
|
||||
if (value === "perfect") {
|
||||
onChange({
|
||||
...verb,
|
||||
tenseCategory: value,
|
||||
tense: isPerfectTense(verb.tense) ? verb.tense : "present perfect",
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
tenseCategory: value,
|
||||
tense: isPerfectTense(vps.verb.tense) ? vps.verb.tense : "present perfect",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
onChange({
|
||||
...verb,
|
||||
tenseCategory: value,
|
||||
tense: isPerfectTense(verb.tense) ? "presentVerb" : verb.tense,
|
||||
...vps,
|
||||
verb: {
|
||||
...vps.verb,
|
||||
tenseCategory: value,
|
||||
tense: isPerfectTense(vps.verb.tense) ? "presentVerb" : vps.verb.tense,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const tOptions = (verb?.tenseCategory === "perfect") ? perfectTenseOptions : tenseOptions;
|
||||
const tOptions = (vps.verb?.tenseCategory === "perfect") ? perfectTenseOptions : verbTenseOptions;
|
||||
return <div>
|
||||
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
||||
<div className="d-flex flex-row justify-content-between align-items-center">
|
||||
<div className="h5">Tense:</div>
|
||||
{verb && <div className="mb-2">
|
||||
{vps.verb && <div className="mb-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
value={verb.tenseCategory}
|
||||
value={vps.verb.tenseCategory}
|
||||
options={[{
|
||||
label: "Basic",
|
||||
value: "basic",
|
||||
|
@ -145,19 +173,19 @@ function TensePicker({ onChange, verb, mode }: {
|
|||
<Select
|
||||
isSearchable={false}
|
||||
// for some reason can't use tOptions with find here;
|
||||
value={verb && ([...tenseOptions, ...perfectTenseOptions].find(o => o.value === verb.tense))}
|
||||
value={vps.verb && ([...verbTenseOptions, ...perfectTenseOptions].find(o => o.value === vps.verb.tense))}
|
||||
onChange={onTenseSelect}
|
||||
className="mb-2"
|
||||
options={tOptions}
|
||||
{...zIndexProps}
|
||||
/>
|
||||
{verb && <div className="d-flex flex-row justify-content-between align-items-center mt-3 mb-1" style={{ width: "100%" }}>
|
||||
{vps.verb && <div className="d-flex flex-row justify-content-between align-items-center mt-3 mb-1" style={{ width: "100%" }}>
|
||||
<div className="btn btn-light clickable" onClick={moveTense("back")}>
|
||||
<i className="fas fa-chevron-left" />
|
||||
</div>
|
||||
{mode !== "charts" && <ButtonSelect
|
||||
small
|
||||
value={verb.negative.toString()}
|
||||
value={vps.verb.negative.toString()}
|
||||
options={[{
|
||||
label: "Pos.",
|
||||
value: "false",
|
||||
|
|
|
@ -5,7 +5,7 @@ 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 }) {
|
||||
function VPDisplay({ VP, opts }: { VP: T.VPSelectionComplete, opts: T.TextOptions }) {
|
||||
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
|
||||
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
||||
const result = compileVP(renderVP(VP), { ...form, OSV });
|
||||
|
@ -46,7 +46,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
|
|||
</div>
|
||||
}
|
||||
|
||||
function whatsAdjustable(VP: T.VPSelection): "both" | "king" | "servant" {
|
||||
function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
|
||||
// TODO: intransitive dynamic compounds?
|
||||
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
|
||||
? (isPastTense(VP.verb.tense) ? "servant" : "king")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import NPPicker from "../np-picker/NPPicker";
|
||||
import VerbPicker from "./VerbPicker";
|
||||
import TensePicker from "./TensePicker";
|
||||
import TensePicker, { getRandomTense } from "./TensePicker";
|
||||
import VPDisplay from "./VPDisplay";
|
||||
import ButtonSelect from "../ButtonSelect";
|
||||
import { renderVP } from "../../lib/phrase-building/index";
|
||||
|
@ -10,7 +10,7 @@ import {
|
|||
import * as T from "../../types";
|
||||
import ChartDisplay from "./ChartDisplay";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import { makeVerbSelection } from "./verb-selection";
|
||||
import { makeVPSelectionState } from "./verb-selection";
|
||||
import { useEffect, useState } from "react";
|
||||
import { randomSubjObj } from "../../library";
|
||||
|
||||
|
@ -29,6 +29,8 @@ const servantEmoji = "🙇♂️";
|
|||
// TODO: option to show 3 modes Phrases - Charts - Quiz
|
||||
|
||||
// TODO: error handling on error with rendering etc
|
||||
type MixState = "NPs" | "tenses" | "both";
|
||||
|
||||
export function VPExplorer(props: {
|
||||
verb: T.VerbEntry,
|
||||
opts: T.TextOptions,
|
||||
|
@ -41,35 +43,22 @@ export function VPExplorer(props: {
|
|||
getNounByTs: (ts: number) => T.NounEntry | undefined,
|
||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined,
|
||||
})) {
|
||||
console.log("passedVerb", props.verb);
|
||||
const [subject, setSubject] = useStickyState<T.NPSelection | undefined>(undefined, "subjectNPSelection");
|
||||
// not quite working with stickyState
|
||||
const [vps, setVps] = useStickyState<T.VPSelectionState>(
|
||||
o => makeVPSelectionState(props.verb, o),
|
||||
"vpsState1",
|
||||
);
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode");
|
||||
const [mix, setMix] = useStickyState<MixState>("NPs", "mixState1");
|
||||
const [showAnswer, setShowAnswer] = useState<boolean>(false);
|
||||
// this isn't quite working
|
||||
// const [verb, setVerb] = useStickyState<T.VerbSelection | undefined>(
|
||||
// passedVerb
|
||||
// ? (old) => makeVerbSelection(passedVerb, setSubject, old)
|
||||
// : undefined,
|
||||
// "verbExplorerVerb",
|
||||
// );
|
||||
const [verb, setVerb] = useState<T.VerbSelection>(
|
||||
makeVerbSelection(props.verb, setSubject)
|
||||
)
|
||||
useEffect(() => {
|
||||
if (mode === "quiz") {
|
||||
if (!verb) setMode("phrases");
|
||||
handleResetQuiz();
|
||||
}
|
||||
// TODO: better system with all this
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
setVerb(o => makeVerbSelection(props.verb, setSubject, o));
|
||||
if (mode === "quiz") {
|
||||
// TODO: Better
|
||||
setMode("charts");
|
||||
}
|
||||
setVps(o => {
|
||||
if (mode === "quiz") {
|
||||
return setRandomQuizState(mix)(
|
||||
makeVPSelectionState(props.verb, o)
|
||||
);
|
||||
}
|
||||
return makeVPSelectionState(props.verb, o);
|
||||
});
|
||||
// eslint-disable-next-line
|
||||
}, [props.verb]);
|
||||
function handleChangeMode(m: "charts" | "phrases" | "quiz") {
|
||||
|
@ -78,46 +67,42 @@ export function VPExplorer(props: {
|
|||
}
|
||||
setMode(m);
|
||||
}
|
||||
function handleSetVerb(v: T.VerbSelection) {
|
||||
if (v?.verb.entry.ts !== verb?.verb.entry.ts) {
|
||||
handleResetQuiz();
|
||||
}
|
||||
setVerb(v);
|
||||
}
|
||||
function handleResetQuiz() {
|
||||
if (!verb) {
|
||||
if (!vps.verb) {
|
||||
alert("Choose a verb to quiz");
|
||||
return;
|
||||
}
|
||||
const { S, V } = setRandomQuizState(subject, verb);
|
||||
setShowAnswer(false);
|
||||
setSubject(S);
|
||||
setVerb(V);
|
||||
setVps(setRandomQuizState(mix));
|
||||
}
|
||||
function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
|
||||
if (!skipPronounConflictCheck && hasPronounConflict(subject, verb?.object)) {
|
||||
if (!skipPronounConflictCheck && hasPronounConflict(subject, vps.verb?.object)) {
|
||||
alert("That combination of pronouns is not allowed");
|
||||
return;
|
||||
}
|
||||
setSubject(subject);
|
||||
setVps(o => ({ ...o, subject }));
|
||||
}
|
||||
function handleObjectChange(object: T.NPSelection | undefined) {
|
||||
if (!verb) return;
|
||||
if ((verb.object === "none") || (typeof verb.object === "number")) return;
|
||||
if (!vps.verb) return;
|
||||
if ((vps.verb.object === "none") || (typeof vps.verb.object === "number")) return;
|
||||
// check for pronoun conflict
|
||||
if (hasPronounConflict(subject, object)) {
|
||||
if (hasPronounConflict(vps.subject, object)) {
|
||||
alert("That combination of pronouns is not allowed");
|
||||
return;
|
||||
}
|
||||
setVerb({ ...verb, object });
|
||||
setVps(o => ({
|
||||
...o,
|
||||
verb: {
|
||||
...o.verb,
|
||||
object,
|
||||
},
|
||||
}));
|
||||
}
|
||||
function handleSubjObjSwap() {
|
||||
if (verb?.isCompound === "dynamic") return;
|
||||
const output = switchSubjObj({ subject, verb });
|
||||
setSubject(output.subject);
|
||||
setVerb(output.verb);
|
||||
if (vps.verb?.isCompound === "dynamic") return;
|
||||
setVps(switchSubjObj)
|
||||
}
|
||||
const verbPhrase: T.VPSelection | undefined = verbPhraseComplete({ subject, verb });
|
||||
const verbPhrase: T.VPSelectionComplete | undefined = completeVPSelection(vps);
|
||||
const VPRendered = verbPhrase && renderVP(verbPhrase);
|
||||
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
||||
<VerbPicker
|
||||
|
@ -127,11 +112,8 @@ export function VPExplorer(props: {
|
|||
} : {
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
verbLocked={!!props.verb}
|
||||
verb={verb}
|
||||
subject={subject}
|
||||
changeSubject={(s) => handleSubjectChange(s, true)}
|
||||
onChange={handleSetVerb}
|
||||
vps={vps}
|
||||
onChange={setVps}
|
||||
opts={props.opts}
|
||||
/>
|
||||
<div className="mt-2 mb-3 text-center">
|
||||
|
@ -145,7 +127,20 @@ export function VPExplorer(props: {
|
|||
handleChange={handleChangeMode}
|
||||
/>
|
||||
</div>
|
||||
{(verb && (typeof verb.object === "object") && (verb.isCompound !== "dynamic") && (mode === "phrases")) &&
|
||||
{mode === "quiz" && <div className="mt-2 mb-3 d-flex flex-row justify-content-center align-items-center">
|
||||
<div className="mr-2">Mix:</div>
|
||||
<ButtonSelect
|
||||
small
|
||||
value={mix}
|
||||
options={[
|
||||
{ label: "NPs", value: "NPs" },
|
||||
{ label: "Tenses", value: "tenses" },
|
||||
{ label: "Both", value: "both" },
|
||||
]}
|
||||
handleChange={setMix}
|
||||
/>
|
||||
</div>}
|
||||
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (mode === "phrases")) &&
|
||||
<div className="text-center mt-4">
|
||||
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
||||
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
||||
|
@ -165,16 +160,16 @@ export function VPExplorer(props: {
|
|||
nouns: props.nouns,
|
||||
verbs: props.verbs,
|
||||
}}
|
||||
np={subject}
|
||||
counterPart={verb ? verb.object : undefined}
|
||||
np={vps.subject}
|
||||
counterPart={vps.verb ? vps.verb.object : undefined}
|
||||
onChange={handleSubjectChange}
|
||||
opts={props.opts}
|
||||
cantClear={mode === "quiz"}
|
||||
/>
|
||||
</div>
|
||||
{verb && (verb.object !== "none") && <div className="my-2">
|
||||
{vps.verb && (vps.verb.object !== "none") && <div className="my-2">
|
||||
<div className="h5 text-center">Object {showRole(VPRendered, "object")}</div>
|
||||
{(typeof verb.object === "number")
|
||||
{(typeof vps.verb.object === "number")
|
||||
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||
: <NPPicker
|
||||
{..."getNounByTs" in props ? {
|
||||
|
@ -187,8 +182,8 @@ export function VPExplorer(props: {
|
|||
verbs: props.verbs,
|
||||
}}
|
||||
asObject
|
||||
np={verb.object}
|
||||
counterPart={subject}
|
||||
np={vps.verb.object}
|
||||
counterPart={vps.subject}
|
||||
onChange={handleObjectChange}
|
||||
opts={props.opts}
|
||||
cantClear={mode === "quiz"}
|
||||
|
@ -197,13 +192,13 @@ export function VPExplorer(props: {
|
|||
</>}
|
||||
<div className="my-2">
|
||||
<TensePicker
|
||||
verb={verb}
|
||||
onChange={handleSetVerb}
|
||||
vps={vps}
|
||||
onChange={setVps}
|
||||
mode={mode}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{(verb && (mode === "quiz")) && <div className="text-center my-2">
|
||||
{(vps.verb && (mode === "quiz")) && <div className="text-center my-2">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
onClick={showAnswer ? handleResetQuiz : () => setShowAnswer(true)}
|
||||
|
@ -216,14 +211,27 @@ export function VPExplorer(props: {
|
|||
{(verbPhrase && ((mode === "phrases") || (mode === "quiz" && showAnswer))) &&
|
||||
<VPDisplay VP={verbPhrase} opts={props.opts} />
|
||||
}
|
||||
{(verb && (mode === "charts")) && <ChartDisplay VS={verb} opts={props.opts} />}
|
||||
{(vps.verb && (mode === "charts")) && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
||||
{mode === "quiz" && <div style={{ height: "300px" }}>
|
||||
{/* spacer for blank space while quizzing */}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default VPExplorer;
|
||||
|
||||
function completeVPSelection(vps: T.VPSelectionState): T.VPSelectionComplete | undefined {
|
||||
if (vps.subject === undefined) return undefined
|
||||
if (vps.verb.object === undefined) return undefined;
|
||||
const verb = vps.verb;
|
||||
return {
|
||||
type: "VPSelectionComplete",
|
||||
subject: vps.subject,
|
||||
object: vps.verb.object,
|
||||
verb,
|
||||
}
|
||||
}
|
||||
|
||||
function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean {
|
||||
const subjPronoun = (subject && subject.type === "pronoun") ? subject : undefined;
|
||||
const objPronoun = (object && typeof object === "object" && object.type === "pronoun") ? object : undefined;
|
||||
|
@ -231,18 +239,6 @@ function hasPronounConflict(subject: T.NPSelection | undefined, object: undefine
|
|||
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
|
||||
}
|
||||
|
||||
function verbPhraseComplete({ subject, verb }: { subject: T.NPSelection | undefined, verb: T.VerbSelection }): T.VPSelection | undefined {
|
||||
if (!subject) return undefined;
|
||||
if (!verb) return undefined;
|
||||
if (verb.object === undefined) return undefined;
|
||||
return {
|
||||
type: "VPSelection",
|
||||
subject,
|
||||
object: verb.object,
|
||||
verb,
|
||||
};
|
||||
}
|
||||
|
||||
function showRole(VP: T.VPRendered | undefined, member: "subject" | "object") {
|
||||
return VP
|
||||
? <span className="ml-2">
|
||||
|
@ -251,8 +247,7 @@ function showRole(VP: T.VPRendered | undefined, member: "subject" | "object") {
|
|||
: "";
|
||||
}
|
||||
|
||||
type SOClump = { subject: T.NPSelection | undefined, verb: T.VerbSelection };
|
||||
function switchSubjObj({ subject, verb }: SOClump): SOClump {
|
||||
function switchSubjObj({ subject, verb }: T.VPSelectionState): T.VPSelectionState {
|
||||
if (!subject|| !verb || !verb.object || !(typeof verb.object === "object")) {
|
||||
return { subject, verb };
|
||||
}
|
||||
|
@ -265,39 +260,41 @@ 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: {
|
||||
function setRandomQuizState(mix: MixState) {
|
||||
return ({ subject, verb }: T.VPSelectionState): T.VPSelectionState => {
|
||||
if (mix === "tenses") {
|
||||
return {
|
||||
subject,
|
||||
verb: randomizeTense(verb, true),
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
const s = randSubj;
|
||||
const v: T.VerbSelection = {
|
||||
...verb,
|
||||
object: (
|
||||
(typeof verb.object === "object" && !(verb.object.type === "noun" && verb.object.dynamicComplement))
|
||||
|
@ -306,6 +303,22 @@ function setRandomQuizState(subject: T.NPSelection | undefined, verb: T.VerbSele
|
|||
)
|
||||
? randObj
|
||||
: verb.object,
|
||||
},
|
||||
}
|
||||
};
|
||||
return {
|
||||
subject: s,
|
||||
verb: mix === "both" ? randomizeTense(v, false) : v,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function randomizeTense(verb: T.VerbSelection, dontRepeatTense: boolean): T.VerbSelection {
|
||||
return {
|
||||
...verb,
|
||||
tense: getRandomTense(
|
||||
// TODO: WHY ISN'T THE OVERLOADING ON THIS
|
||||
// @ts-ignore
|
||||
verb.tenseCategory,
|
||||
dontRepeatTense ? verb.tense : undefined,
|
||||
),
|
||||
};
|
||||
}
|
|
@ -3,93 +3,68 @@ import ButtonSelect from "../ButtonSelect";
|
|||
import { RootsAndStems } from "../verb-info/VerbInfo";
|
||||
import { getVerbInfo } from "../../lib/verb-info";
|
||||
import Hider from "../Hider";
|
||||
import { makeVerbSelection } from "./verb-selection";
|
||||
import EntrySelect from "../EntrySelect";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
|
||||
|
||||
// TODO: dark on past tense selecitons
|
||||
|
||||
function VerbPicker(props: ({
|
||||
verbs: T.VerbEntry[],
|
||||
} | {
|
||||
verbs: (s: string) => T.VerbEntry[],
|
||||
getVerbByTs: (ts: number) => T.VerbEntry | undefined;
|
||||
}) & {
|
||||
verb: T.VerbSelection,
|
||||
subject: T.NPSelection | undefined,
|
||||
onChange: (p: T.VerbSelection) => void,
|
||||
changeSubject: (p: T.NPSelection | undefined) => void,
|
||||
function VerbPicker(props: {
|
||||
vps: T.VPSelectionState,
|
||||
onChange: (p: T.VPSelectionState) => void,
|
||||
opts: T.TextOptions,
|
||||
verbLocked: boolean,
|
||||
}) {
|
||||
const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(false, "showRootsAndStems");
|
||||
const infoRaw = props.verb ? getVerbInfo(props.verb.verb.entry, props.verb.verb.complement) : undefined;
|
||||
const info = (!infoRaw || !props.verb)
|
||||
const infoRaw = props.vps.verb ? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement) : undefined;
|
||||
const info = (!infoRaw || !props.vps.verb)
|
||||
? undefined
|
||||
: ("stative" in infoRaw)
|
||||
? infoRaw[props.verb.isCompound === "stative" ? "stative" : "dynamic"]
|
||||
? infoRaw[props.vps.verb.isCompound === "stative" ? "stative" : "dynamic"]
|
||||
: ("transitive" in infoRaw)
|
||||
? infoRaw[props.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
|
||||
? infoRaw[props.vps.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
|
||||
: infoRaw;
|
||||
if (info && ("stative" in info || "transitive" in info)) {
|
||||
return <div>ERROR: Verb version should be select first</div>;
|
||||
}
|
||||
// const [filters, useFilters] = useState<Filters>({
|
||||
// stative: true,
|
||||
// dynamic: true,
|
||||
// transitive: true,
|
||||
// intransitive: true,
|
||||
// grammaticallyTransitive: true,
|
||||
// });
|
||||
function onVerbSelect(v: T.VerbEntry | undefined) {
|
||||
// TODO: what to do when clearing
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
props.onChange(makeVerbSelection(v, props.changeSubject, props.verb));
|
||||
}
|
||||
function onVoiceSelect(value: "active" | "passive") {
|
||||
if (props.verb && props.verb.changeVoice) {
|
||||
if (value === "passive" && (typeof props.verb.object === "object")) {
|
||||
props.changeSubject(props.verb.object);
|
||||
if (props.vps.verb && props.vps.verb.changeVoice) {
|
||||
if (value === "passive" && (typeof props.vps.verb.object === "object")) {
|
||||
props.onChange({
|
||||
...props.vps,
|
||||
subject: props.vps.verb.object,
|
||||
})
|
||||
}
|
||||
if (value === "active") {
|
||||
props.changeSubject(undefined);
|
||||
props.onChange({
|
||||
...props.vps,
|
||||
subject: undefined,
|
||||
});
|
||||
}
|
||||
props.onChange(props.verb.changeVoice(value, value === "active" ? props.subject : undefined));
|
||||
props.onChange({
|
||||
...props.vps,
|
||||
verb: props.vps.verb.changeVoice(value, value === "active" ? props.vps.subject : undefined),
|
||||
});
|
||||
}
|
||||
}
|
||||
function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" {
|
||||
return t === "intransitive" ? "transitive" : t;
|
||||
}
|
||||
function handleChangeTransitivity(t: "transitive" | "grammatically transitive") {
|
||||
if (props.verb && props.verb.changeTransitivity) {
|
||||
props.onChange(props.verb.changeTransitivity(t));
|
||||
if (props.vps.verb && props.vps.verb.changeTransitivity) {
|
||||
props.onChange({
|
||||
...props.vps,
|
||||
verb: props.vps.verb.changeTransitivity(t),
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleChangeStatDyn(c: "stative" | "dynamic") {
|
||||
if (props.verb && props.verb.changeStatDyn) {
|
||||
props.onChange(props.verb.changeStatDyn(c));
|
||||
if (props.vps.verb && props.vps.verb.changeStatDyn) {
|
||||
props.onChange({
|
||||
...props.vps,
|
||||
verb: props.vps.verb.changeStatDyn(c),
|
||||
});
|
||||
}
|
||||
}
|
||||
return <div className="mb-3">
|
||||
{!props.verbLocked && <div style={{ maxWidth: "300px", margin: "0 auto" }}>
|
||||
<div className="h5">Verb:</div>
|
||||
<EntrySelect
|
||||
{..."getVerbByTs" in props ? {
|
||||
searchF: props.verbs,
|
||||
getByTs: props.getVerbByTs,
|
||||
} : {
|
||||
entries: props.verbs,
|
||||
}}
|
||||
value={props.verb?.verb}
|
||||
onChange={onVerbSelect}
|
||||
name="Verb"
|
||||
isVerbSelect
|
||||
opts={props.opts}
|
||||
/>
|
||||
</div>}
|
||||
{info && <div className="mt-3 mb-1 text-center">
|
||||
<Hider
|
||||
showing={showRootsAndStems}
|
||||
|
@ -104,7 +79,7 @@ function VerbPicker(props: ({
|
|||
</Hider>
|
||||
</div>}
|
||||
<div className="d-flex flex-row justify-content-around flex-wrap" style={{ maxWidth: "400px", margin: "0 auto" }}>
|
||||
{props.verb && props.verb.changeTransitivity && <div className="text-center my-2">
|
||||
{props.vps.verb && props.vps.verb.changeTransitivity && <div className="text-center my-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[{
|
||||
|
@ -114,14 +89,14 @@ function VerbPicker(props: ({
|
|||
label: "trans.",
|
||||
value: "transitive",
|
||||
}]}
|
||||
value={notInstransitive(props.verb.transitivity)}
|
||||
value={notInstransitive(props.vps.verb.transitivity)}
|
||||
handleChange={handleChangeTransitivity}
|
||||
/>
|
||||
</div>}
|
||||
{props.verb && props.verb.changeVoice && <div className="text-center my-2">
|
||||
{props.vps.verb && props.vps.verb.changeVoice && <div className="text-center my-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
value={props.verb.voice}
|
||||
value={props.vps.verb.voice}
|
||||
options={[{
|
||||
label: "Active",
|
||||
value: "active",
|
||||
|
@ -132,7 +107,7 @@ function VerbPicker(props: ({
|
|||
handleChange={onVoiceSelect}
|
||||
/>
|
||||
</div>}
|
||||
{props.verb && props.verb.changeStatDyn && <div className="text-center my-2">
|
||||
{props.vps.verb && props.vps.verb.changeStatDyn && <div className="text-center my-2">
|
||||
<ButtonSelect
|
||||
small
|
||||
options={[{
|
||||
|
@ -142,7 +117,7 @@ function VerbPicker(props: ({
|
|||
label: "dynamic",
|
||||
value: "dynamic",
|
||||
}]}
|
||||
value={props.verb.isCompound ? props.verb.isCompound : "stative"}
|
||||
value={props.vps.verb.isCompound ? props.vps.verb.isCompound : "stative"}
|
||||
handleChange={handleChangeStatDyn}
|
||||
/>
|
||||
</div>}
|
||||
|
|
|
@ -5,31 +5,34 @@ import * as T from "../../types";
|
|||
import { getVerbInfo } from "../../lib/verb-info";
|
||||
import { isPerfectTense } from "../../lib/phrase-building/vp-tools";
|
||||
|
||||
export function makeVerbSelection(verb: T.VerbEntry, changeSubject: (s: T.NPSelection | undefined) => void, oldVerbSelection?: T.VerbSelection): T.VerbSelection {
|
||||
export function makeVPSelectionState(
|
||||
verb: T.VerbEntry,
|
||||
os?: T.VPSelectionState,
|
||||
): T.VPSelectionState {
|
||||
const info = getVerbInfo(verb.entry, verb.complement);
|
||||
function getTransObjFromOldVerbSelection() {
|
||||
const subject = (os?.verb.voice === "passive" && info.type === "dynamic compound")
|
||||
? makeNounSelection(info.objComplement.entry as T.NounEntry, true)
|
||||
: (os?.subject || undefined);
|
||||
function getTransObjFromos() {
|
||||
if (
|
||||
!oldVerbSelection ||
|
||||
oldVerbSelection.object === "none" ||
|
||||
typeof oldVerbSelection.object === "number" ||
|
||||
oldVerbSelection.isCompound === "dynamic" ||
|
||||
(oldVerbSelection.object?.type === "noun" && oldVerbSelection.object.dynamicComplement)
|
||||
!os ||
|
||||
os.verb.object === "none" ||
|
||||
typeof os.verb.object === "number" ||
|
||||
os.verb.isCompound === "dynamic" ||
|
||||
(os.verb.object?.type === "noun" && os.verb.object.dynamicComplement)
|
||||
) return undefined;
|
||||
return oldVerbSelection.object;
|
||||
return os.verb.object;
|
||||
}
|
||||
const transitivity: T.Transitivity = "grammaticallyTransitive" in info
|
||||
? "transitive"
|
||||
: info.transitivity;
|
||||
const object = (transitivity === "grammatically transitive")
|
||||
? T.Person.ThirdPlurMale
|
||||
: (info.type === "dynamic compound" && oldVerbSelection?.voice !== "passive")
|
||||
: (info.type === "dynamic compound" && os?.verb.voice !== "passive")
|
||||
? makeNounSelection(info.objComplement.entry as T.NounEntry, true)
|
||||
: (transitivity === "transitive" && oldVerbSelection?.voice !== "passive")
|
||||
? getTransObjFromOldVerbSelection()
|
||||
: (transitivity === "transitive" && os?.verb.voice !== "passive")
|
||||
? getTransObjFromos()
|
||||
: "none";
|
||||
if (oldVerbSelection?.voice === "passive" && info.type === "dynamic compound") {
|
||||
changeSubject(makeNounSelection(info.objComplement.entry as T.NounEntry, true));
|
||||
}
|
||||
const isCompound = ("stative" in info || info.type === "stative compound")
|
||||
? "stative"
|
||||
: info.type === "dynamic compound"
|
||||
|
@ -47,60 +50,63 @@ export function makeVerbSelection(verb: T.VerbEntry, changeSubject: (s: T.NPSele
|
|||
tenseCategory: "basic" | "modal",
|
||||
tense: T.VerbTense,
|
||||
} => {
|
||||
if (!oldVerbSelection) {
|
||||
if (!os) {
|
||||
return { tense: "presentVerb", tenseCategory: "basic" };
|
||||
}
|
||||
if (oldVerbSelection.tenseCategory === "modal") {
|
||||
return { tenseCategory: "modal", tense: isPerfectTense(oldVerbSelection.tense) ? "presentVerb" : oldVerbSelection.tense };
|
||||
if (os.verb.tenseCategory === "modal") {
|
||||
return { tenseCategory: "modal", tense: isPerfectTense(os.verb.tense) ? "presentVerb" : os.verb.tense };
|
||||
}
|
||||
if (oldVerbSelection.tenseCategory === "basic") {
|
||||
return { tenseCategory: "basic", tense: isPerfectTense(oldVerbSelection.tense) ? "presentVerb" : oldVerbSelection.tense };
|
||||
if (os.verb.tenseCategory === "basic") {
|
||||
return { tenseCategory: "basic", tense: isPerfectTense(os.verb.tense) ? "presentVerb" : os.verb.tense };
|
||||
}
|
||||
return { tenseCategory: "perfect", tense: isPerfectTense(oldVerbSelection.tense) ? oldVerbSelection.tense : "present perfect" };
|
||||
return { tenseCategory: "perfect", tense: isPerfectTense(os.verb.tense) ? os.verb.tense : "present perfect" };
|
||||
})();
|
||||
return {
|
||||
type: "verb",
|
||||
verb: verb,
|
||||
dynAuxVerb,
|
||||
...tenseSelection,
|
||||
object,
|
||||
transitivity,
|
||||
isCompound,
|
||||
voice: transitivity === "transitive"
|
||||
? (oldVerbSelection?.voice || "active")
|
||||
: "active",
|
||||
negative: oldVerbSelection ? oldVerbSelection.negative : false,
|
||||
...("grammaticallyTransitive" in info) ? {
|
||||
changeTransitivity: function(t) {
|
||||
return {
|
||||
...this,
|
||||
transitivity: t,
|
||||
object: t === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
||||
};
|
||||
},
|
||||
} : {},
|
||||
...("stative" in info) ? {
|
||||
changeStatDyn: function(c) {
|
||||
return {
|
||||
...this,
|
||||
isCompound: c,
|
||||
object: c === "dynamic"
|
||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, true)
|
||||
: undefined,
|
||||
dynAuxVerb: c === "dynamic"
|
||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
} : {},
|
||||
...(transitivity === "transitive") ? {
|
||||
changeVoice: function(v, s) {
|
||||
return {
|
||||
...this,
|
||||
voice: v,
|
||||
object: v === "active" ? s : "none",
|
||||
};
|
||||
},
|
||||
} : {},
|
||||
subject,
|
||||
verb: {
|
||||
type: "verb",
|
||||
verb: verb,
|
||||
dynAuxVerb,
|
||||
...tenseSelection,
|
||||
object,
|
||||
transitivity,
|
||||
isCompound,
|
||||
voice: transitivity === "transitive"
|
||||
? (os?.verb.voice || "active")
|
||||
: "active",
|
||||
negative: os ? os.verb.negative : false,
|
||||
...("grammaticallyTransitive" in info) ? {
|
||||
changeTransitivity: function(t) {
|
||||
return {
|
||||
...this,
|
||||
transitivity: t,
|
||||
object: t === "grammatically transitive" ? T.Person.ThirdPlurMale : undefined,
|
||||
};
|
||||
},
|
||||
} : {},
|
||||
...("stative" in info) ? {
|
||||
changeStatDyn: function(c) {
|
||||
return {
|
||||
...this,
|
||||
isCompound: c,
|
||||
object: c === "dynamic"
|
||||
? makeNounSelection(info.dynamic.objComplement.entry as T.NounEntry, true)
|
||||
: undefined,
|
||||
dynAuxVerb: c === "dynamic"
|
||||
? { entry: info.dynamic.auxVerb } as T.VerbEntry
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
} : {},
|
||||
...(transitivity === "transitive") ? {
|
||||
changeVoice: function(v, s) {
|
||||
return {
|
||||
...this,
|
||||
voice: v,
|
||||
object: v === "active" ? s : "none",
|
||||
};
|
||||
},
|
||||
} : {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import { renderEnglishVPBase } from "./english-vp-rendering";
|
|||
|
||||
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS
|
||||
|
||||
export function renderVP(VP: T.VPSelection): T.VPRendered {
|
||||
export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
||||
// Sentence Rules Logic
|
||||
const isPast = isPastTense(VP.verb.tense);
|
||||
const isTransitive = VP.object !== "none";
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
import {
|
||||
getVerbInfo,
|
||||
} from "./lib/verb-info";
|
||||
import { makeVerbSelection } from "./components/vp-explorer/verb-selection";
|
||||
import ConjugationViewer from "./components/ConjugationViewer";
|
||||
import InflectionsTable from "./components/InflectionsTable";
|
||||
import Pashto from "./components/Pashto";
|
||||
|
@ -164,7 +163,6 @@ export {
|
|||
capitalizeFirstLetter,
|
||||
psStringFromEntry,
|
||||
getLong,
|
||||
makeVerbSelection,
|
||||
useStickyState,
|
||||
randomPerson,
|
||||
isInvalidSubjObjCombo,
|
||||
|
|
19
src/types.ts
19
src/types.ts
|
@ -508,13 +508,6 @@ export type Words = {
|
|||
adverbs: AdverbEntry[],
|
||||
}
|
||||
|
||||
export type VPSelection = {
|
||||
type: "VPSelection",
|
||||
subject: NPSelection,
|
||||
object: Exclude<VerbObject, undefined>,
|
||||
verb: Exclude<VerbSelection, "object">,
|
||||
};
|
||||
|
||||
// TODO: make this Rendered<VPSelection> with recursive Rendered<>
|
||||
export type VPRendered = {
|
||||
type: "VPRendered",
|
||||
|
@ -543,6 +536,18 @@ export type NounNumber = "singular" | "plural";
|
|||
|
||||
export type PerfectTense = `${EquativeTense} perfect`;
|
||||
|
||||
export type VPSelectionState = {
|
||||
subject: NPSelection | undefined,
|
||||
verb: VerbSelection,
|
||||
};
|
||||
|
||||
export type VPSelectionComplete = {
|
||||
type: "VPSelectionComplete",
|
||||
subject: NPSelection,
|
||||
object: Exclude<VerbObject, undefined>,
|
||||
verb: Exclude<VerbSelection, "object">,
|
||||
};
|
||||
|
||||
export type VerbSelection = {
|
||||
type: "verb",
|
||||
verb: VerbEntry,
|
||||
|
|
Loading…
Reference in New Issue