proper quizzin

This commit is contained in:
lingdocs 2022-04-11 17:16:30 +05:00
parent a2a0200a38
commit 3f58fb6bef
9 changed files with 310 additions and 281 deletions

View File

@ -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 => (

View File

@ -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",

View File

@ -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")

View File

@ -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,
),
};
}

View File

@ -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>}

View File

@ -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",
};
},
} : {},
},
};
}

View File

@ -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";

View File

@ -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,

View File

@ -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,