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") { if (transitivity === "grammatically transitive") {
setVerbTypeShowing("simple"); setVerbTypeShowing("simple");
} }
if (transitivity === "intransitive" && verbTypeShowing === "dynamic compound") {
setTransitivityShowing("transitive");
return;
}
setTransitivityShowing(e.target.value as T.Transitivity); setTransitivityShowing(e.target.value as T.Transitivity);
} }
const isRegularVerb = (entry: T.DictionaryEntry): boolean => ( const isRegularVerb = (entry: T.DictionaryEntry): boolean => (

View File

@ -6,7 +6,7 @@ import * as T from "../../types";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
import { isPerfectTense } from "../../lib/phrase-building/vp-tools"; 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>, label: <div><i className="fas fa-video mr-2" />present</div>,
value: "presentVerb", value: "presentVerb",
}, { }, {
@ -55,34 +55,53 @@ const perfectTenseOptions: { label: string | JSX.Element, value: T.PerfectTense
value: "pastSubjunctive perfect", value: "pastSubjunctive perfect",
}]; }];
function TensePicker({ onChange, verb, mode }: { export function getRandomTense(type: "basic" | "modal", o?: T.VerbTense): T.VerbTense;
verb: T.VerbSelection, export function getRandomTense(type: "perfect", o?: T.PerfectTense | T.VerbTense): T.PerfectTense;
onChange: (p: T.VerbSelection) => void, 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", 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;
if (verb && value) { if (vps.verb && value) {
if (isPerfectTense(value)) { if (isPerfectTense(value)) {
onChange({ onChange({
...verb, ...vps,
verb: {
...vps.verb,
tense: value, tense: value,
tenseCategory: "perfect", tenseCategory: "perfect",
},
}); });
} else { } else {
onChange({ onChange({
...verb, ...vps,
verb: {
...vps.verb,
tense: value, tense: value,
tenseCategory: verb.tenseCategory === "perfect" ? "basic" : verb.tenseCategory, tenseCategory: vps.verb.tenseCategory === "perfect" ? "basic" : vps.verb.tenseCategory,
},
}); });
} }
} }
} }
function moveTense(dir: "forward" | "back") { function moveTense(dir: "forward" | "back") {
if (!verb) return; if (!vps.verb) return;
return () => { return () => {
const tenses = verb.tenseCategory === "perfect" ? perfectTenseOptions : tenseOptions; const tenses = vps.verb.tenseCategory === "perfect" ? perfectTenseOptions : verbTenseOptions;
const currIndex = tenses.findIndex(tn => tn.value === verb.tense) const currIndex = tenses.findIndex(tn => tn.value === vps.verb.tense)
if (currIndex === -1) { if (currIndex === -1) {
console.error("error moving tense", dir); console.error("error moving tense", dir);
return; return;
@ -95,39 +114,48 @@ function TensePicker({ onChange, verb, mode }: {
}; };
} }
function onPosNegSelect(value: string) { function onPosNegSelect(value: string) {
if (verb) { if (vps.verb) {
onChange({ onChange({
...verb, ...vps,
verb: {
...vps.verb,
negative: value === "true", negative: value === "true",
},
}); });
} }
} }
function onTenseCategorySelect(value: "basic" | "modal" | "perfect") { function onTenseCategorySelect(value: "basic" | "modal" | "perfect") {
if (verb) { if (vps.verb) {
if (value === "perfect") { if (value === "perfect") {
onChange({ onChange({
...verb, ...vps,
verb: {
...vps.verb,
tenseCategory: value, tenseCategory: value,
tense: isPerfectTense(verb.tense) ? verb.tense : "present perfect", tense: isPerfectTense(vps.verb.tense) ? vps.verb.tense : "present perfect",
},
}); });
} else { } else {
onChange({ onChange({
...verb, ...vps,
verb: {
...vps.verb,
tenseCategory: value, tenseCategory: value,
tense: isPerfectTense(verb.tense) ? "presentVerb" : verb.tense, 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> return <div>
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}> <div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
<div className="d-flex flex-row justify-content-between align-items-center"> <div className="d-flex flex-row justify-content-between align-items-center">
<div className="h5">Tense:</div> <div className="h5">Tense:</div>
{verb && <div className="mb-2"> {vps.verb && <div className="mb-2">
<ButtonSelect <ButtonSelect
small small
value={verb.tenseCategory} value={vps.verb.tenseCategory}
options={[{ options={[{
label: "Basic", label: "Basic",
value: "basic", value: "basic",
@ -145,19 +173,19 @@ function TensePicker({ onChange, verb, mode }: {
<Select <Select
isSearchable={false} isSearchable={false}
// for some reason can't use tOptions with find here; // 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} onChange={onTenseSelect}
className="mb-2" className="mb-2"
options={tOptions} options={tOptions}
{...zIndexProps} {...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")}> <div className="btn btn-light clickable" onClick={moveTense("back")}>
<i className="fas fa-chevron-left" /> <i className="fas fa-chevron-left" />
</div> </div>
{mode !== "charts" && <ButtonSelect {mode !== "charts" && <ButtonSelect
small small
value={verb.negative.toString()} value={vps.verb.negative.toString()}
options={[{ options={[{
label: "Pos.", label: "Pos.",
value: "false", value: "false",

View File

@ -5,7 +5,7 @@ import AbbreviationFormSelector from "./AbbreviationFormSelector";
import { isPastTense } from "../../lib/phrase-building/vp-tools"; import { isPastTense } from "../../lib/phrase-building/vp-tools";
import { useStickyState } from "../../library"; 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 [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV"); const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
const result = compileVP(renderVP(VP), { ...form, OSV }); const result = compileVP(renderVP(VP), { ...form, OSV });
@ -46,7 +46,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelection, opts: T.TextOptions }) {
</div> </div>
} }
function whatsAdjustable(VP: T.VPSelection): "both" | "king" | "servant" { function whatsAdjustable(VP: T.VPSelectionComplete): "both" | "king" | "servant" {
// TODO: intransitive dynamic compounds? // TODO: intransitive dynamic compounds?
return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive") return (VP.verb.isCompound === "dynamic" && VP.verb.transitivity === "transitive")
? (isPastTense(VP.verb.tense) ? "servant" : "king") ? (isPastTense(VP.verb.tense) ? "servant" : "king")

View File

@ -1,6 +1,6 @@
import NPPicker from "../np-picker/NPPicker"; import NPPicker from "../np-picker/NPPicker";
import VerbPicker from "./VerbPicker"; import VerbPicker from "./VerbPicker";
import TensePicker from "./TensePicker"; import TensePicker, { getRandomTense } from "./TensePicker";
import VPDisplay from "./VPDisplay"; import VPDisplay from "./VPDisplay";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
import { renderVP } from "../../lib/phrase-building/index"; import { renderVP } from "../../lib/phrase-building/index";
@ -10,7 +10,7 @@ import {
import * as T from "../../types"; 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 { makeVPSelectionState } from "./verb-selection";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { randomSubjObj } from "../../library"; import { randomSubjObj } from "../../library";
@ -29,6 +29,8 @@ const servantEmoji = "🙇‍♂️";
// TODO: option to show 3 modes Phrases - Charts - Quiz // TODO: option to show 3 modes Phrases - Charts - Quiz
// TODO: error handling on error with rendering etc // TODO: error handling on error with rendering etc
type MixState = "NPs" | "tenses" | "both";
export function VPExplorer(props: { export function VPExplorer(props: {
verb: T.VerbEntry, verb: T.VerbEntry,
opts: T.TextOptions, opts: T.TextOptions,
@ -41,35 +43,22 @@ export function VPExplorer(props: {
getNounByTs: (ts: number) => T.NounEntry | undefined, getNounByTs: (ts: number) => T.NounEntry | undefined,
getVerbByTs: (ts: number) => T.VerbEntry | undefined, getVerbByTs: (ts: number) => T.VerbEntry | undefined,
})) { })) {
console.log("passedVerb", props.verb); const [vps, setVps] = useStickyState<T.VPSelectionState>(
const [subject, setSubject] = useStickyState<T.NPSelection | undefined>(undefined, "subjectNPSelection"); o => makeVPSelectionState(props.verb, o),
// not quite working with stickyState "vpsState1",
);
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode"); const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">("phrases", "verbExplorerMode");
const [mix, setMix] = useStickyState<MixState>("NPs", "mixState1");
const [showAnswer, setShowAnswer] = useState<boolean>(false); 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(() => { useEffect(() => {
setVps(o => {
if (mode === "quiz") { if (mode === "quiz") {
if (!verb) setMode("phrases"); return setRandomQuizState(mix)(
handleResetQuiz(); makeVPSelectionState(props.verb, o)
} );
// 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");
} }
return makeVPSelectionState(props.verb, o);
});
// eslint-disable-next-line // eslint-disable-next-line
}, [props.verb]); }, [props.verb]);
function handleChangeMode(m: "charts" | "phrases" | "quiz") { function handleChangeMode(m: "charts" | "phrases" | "quiz") {
@ -78,46 +67,42 @@ export function VPExplorer(props: {
} }
setMode(m); setMode(m);
} }
function handleSetVerb(v: T.VerbSelection) {
if (v?.verb.entry.ts !== verb?.verb.entry.ts) {
handleResetQuiz();
}
setVerb(v);
}
function handleResetQuiz() { function handleResetQuiz() {
if (!verb) { if (!vps.verb) {
alert("Choose a verb to quiz"); alert("Choose a verb to quiz");
return; return;
} }
const { S, V } = setRandomQuizState(subject, verb);
setShowAnswer(false); setShowAnswer(false);
setSubject(S); setVps(setRandomQuizState(mix));
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, vps.verb?.object)) {
alert("That combination of pronouns is not allowed"); alert("That combination of pronouns is not allowed");
return; return;
} }
setSubject(subject); setVps(o => ({ ...o, subject }));
} }
function handleObjectChange(object: T.NPSelection | undefined) { function handleObjectChange(object: T.NPSelection | undefined) {
if (!verb) return; if (!vps.verb) return;
if ((verb.object === "none") || (typeof verb.object === "number")) return; if ((vps.verb.object === "none") || (typeof vps.verb.object === "number")) return;
// check for pronoun conflict // check for pronoun conflict
if (hasPronounConflict(subject, object)) { if (hasPronounConflict(vps.subject, object)) {
alert("That combination of pronouns is not allowed"); alert("That combination of pronouns is not allowed");
return; return;
} }
setVerb({ ...verb, object }); setVps(o => ({
...o,
verb: {
...o.verb,
object,
},
}));
} }
function handleSubjObjSwap() { function handleSubjObjSwap() {
if (verb?.isCompound === "dynamic") return; if (vps.verb?.isCompound === "dynamic") return;
const output = switchSubjObj({ subject, verb }); setVps(switchSubjObj)
setSubject(output.subject);
setVerb(output.verb);
} }
const verbPhrase: T.VPSelection | undefined = verbPhraseComplete({ subject, verb }); const verbPhrase: T.VPSelectionComplete | undefined = completeVPSelection(vps);
const VPRendered = verbPhrase && renderVP(verbPhrase); const VPRendered = verbPhrase && renderVP(verbPhrase);
return <div className="mt-3" style={{ maxWidth: "950px"}}> return <div className="mt-3" style={{ maxWidth: "950px"}}>
<VerbPicker <VerbPicker
@ -127,11 +112,8 @@ export function VPExplorer(props: {
} : { } : {
verbs: props.verbs, verbs: props.verbs,
}} }}
verbLocked={!!props.verb} vps={vps}
verb={verb} onChange={setVps}
subject={subject}
changeSubject={(s) => handleSubjectChange(s, true)}
onChange={handleSetVerb}
opts={props.opts} opts={props.opts}
/> />
<div className="mt-2 mb-3 text-center"> <div className="mt-2 mb-3 text-center">
@ -145,7 +127,20 @@ export function VPExplorer(props: {
handleChange={handleChangeMode} handleChange={handleChangeMode}
/> />
</div> </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"> <div className="text-center mt-4">
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light"> <button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
<i className="fas fa-exchange-alt mr-2" /> subj/obj <i className="fas fa-exchange-alt mr-2" /> subj/obj
@ -165,16 +160,16 @@ export function VPExplorer(props: {
nouns: props.nouns, nouns: props.nouns,
verbs: props.verbs, verbs: props.verbs,
}} }}
np={subject} np={vps.subject}
counterPart={verb ? verb.object : undefined} counterPart={vps.verb ? vps.verb.object : undefined}
onChange={handleSubjectChange} onChange={handleSubjectChange}
opts={props.opts} opts={props.opts}
cantClear={mode === "quiz"} cantClear={mode === "quiz"}
/> />
</div> </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> <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> ? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker : <NPPicker
{..."getNounByTs" in props ? { {..."getNounByTs" in props ? {
@ -187,8 +182,8 @@ export function VPExplorer(props: {
verbs: props.verbs, verbs: props.verbs,
}} }}
asObject asObject
np={verb.object} np={vps.verb.object}
counterPart={subject} counterPart={vps.subject}
onChange={handleObjectChange} onChange={handleObjectChange}
opts={props.opts} opts={props.opts}
cantClear={mode === "quiz"} cantClear={mode === "quiz"}
@ -197,13 +192,13 @@ export function VPExplorer(props: {
</>} </>}
<div className="my-2"> <div className="my-2">
<TensePicker <TensePicker
verb={verb} vps={vps}
onChange={handleSetVerb} onChange={setVps}
mode={mode} mode={mode}
/> />
</div> </div>
</div> </div>
{(verb && (mode === "quiz")) && <div className="text-center my-2"> {(vps.verb && (mode === "quiz")) && <div className="text-center my-2">
<button <button
className="btn btn-primary" className="btn btn-primary"
onClick={showAnswer ? handleResetQuiz : () => setShowAnswer(true)} onClick={showAnswer ? handleResetQuiz : () => setShowAnswer(true)}
@ -216,14 +211,27 @@ export function VPExplorer(props: {
{(verbPhrase && ((mode === "phrases") || (mode === "quiz" && showAnswer))) && {(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} />} {(vps.verb && (mode === "charts")) && <ChartDisplay VS={vps.verb} opts={props.opts} />}
{mode === "quiz" && <div style={{ height: "300px" }}> {mode === "quiz" && <div style={{ height: "300px" }}>
{/* spacer for blank space while quizzing */}
</div>} </div>}
</div> </div>
} }
export default VPExplorer; 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 { function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean {
const subjPronoun = (subject && subject.type === "pronoun") ? subject : undefined; const subjPronoun = (subject && subject.type === "pronoun") ? subject : undefined;
const objPronoun = (object && typeof object === "object" && object.type === "pronoun") ? object : 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); 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") { function showRole(VP: T.VPRendered | undefined, member: "subject" | "object") {
return VP return VP
? <span className="ml-2"> ? <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 }: T.VPSelectionState): T.VPSelectionState {
function switchSubjObj({ subject, verb }: SOClump): SOClump {
if (!subject|| !verb || !verb.object || !(typeof verb.object === "object")) { if (!subject|| !verb || !verb.object || !(typeof verb.object === "object")) {
return { subject, verb }; return { subject, verb };
} }
@ -265,10 +260,14 @@ function switchSubjObj({ subject, verb }: SOClump): SOClump {
}; };
} }
function setRandomQuizState(subject: T.NPSelection | undefined, verb: T.VerbSelection): { function setRandomQuizState(mix: MixState) {
S: T.NPSelection, return ({ subject, verb }: T.VPSelectionState): T.VPSelectionState => {
V: T.VerbSelection, if (mix === "tenses") {
} { return {
subject,
verb: randomizeTense(verb, true),
}
}
const oldSubj = (subject?.type === "pronoun") const oldSubj = (subject?.type === "pronoun")
? subject.person ? subject.person
: undefined; : undefined;
@ -294,10 +293,8 @@ function setRandomQuizState(subject: T.NPSelection | undefined, verb: T.VerbSele
distance: "far", distance: "far",
person: obj, person: obj,
}; };
return { const s = randSubj;
// TODO: Randomize the near/far ?? const v: T.VerbSelection = {
S: randSubj,
V: {
...verb, ...verb,
object: ( object: (
(typeof verb.object === "object" && !(verb.object.type === "noun" && verb.object.dynamicComplement)) (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 ? randObj
: verb.object, : 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 { RootsAndStems } from "../verb-info/VerbInfo";
import { getVerbInfo } from "../../lib/verb-info"; import { getVerbInfo } from "../../lib/verb-info";
import Hider from "../Hider"; import Hider from "../Hider";
import { makeVerbSelection } from "./verb-selection";
import EntrySelect from "../EntrySelect";
import useStickyState from "../../lib/useStickyState"; import useStickyState from "../../lib/useStickyState";
// TODO: dark on past tense selecitons // TODO: dark on past tense selecitons
function VerbPicker(props: ({ function VerbPicker(props: {
verbs: T.VerbEntry[], vps: T.VPSelectionState,
} | { onChange: (p: T.VPSelectionState) => void,
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,
opts: T.TextOptions, opts: T.TextOptions,
verbLocked: boolean,
}) { }) {
const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(false, "showRootsAndStems"); const [showRootsAndStems, setShowRootsAndStems] = useStickyState<boolean>(false, "showRootsAndStems");
const infoRaw = props.verb ? getVerbInfo(props.verb.verb.entry, props.verb.verb.complement) : undefined; const infoRaw = props.vps.verb ? getVerbInfo(props.vps.verb.verb.entry, props.vps.verb.verb.complement) : undefined;
const info = (!infoRaw || !props.verb) const info = (!infoRaw || !props.vps.verb)
? undefined ? undefined
: ("stative" in infoRaw) : ("stative" in infoRaw)
? infoRaw[props.verb.isCompound === "stative" ? "stative" : "dynamic"] ? infoRaw[props.vps.verb.isCompound === "stative" ? "stative" : "dynamic"]
: ("transitive" in infoRaw) : ("transitive" in infoRaw)
? infoRaw[props.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"] ? infoRaw[props.vps.verb.transitivity === "grammatically transitive" ? "grammaticallyTransitive" : "transitive"]
: infoRaw; : infoRaw;
if (info && ("stative" in info || "transitive" in info)) { if (info && ("stative" in info || "transitive" in info)) {
return <div>ERROR: Verb version should be select first</div>; 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") { function onVoiceSelect(value: "active" | "passive") {
if (props.verb && props.verb.changeVoice) { if (props.vps.verb && props.vps.verb.changeVoice) {
if (value === "passive" && (typeof props.verb.object === "object")) { if (value === "passive" && (typeof props.vps.verb.object === "object")) {
props.changeSubject(props.verb.object); props.onChange({
...props.vps,
subject: props.vps.verb.object,
})
} }
if (value === "active") { 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" { function notInstransitive(t: "transitive" | "intransitive" | "grammatically transitive"): "transitive" | "grammatically transitive" {
return t === "intransitive" ? "transitive" : t; return t === "intransitive" ? "transitive" : t;
} }
function handleChangeTransitivity(t: "transitive" | "grammatically transitive") { function handleChangeTransitivity(t: "transitive" | "grammatically transitive") {
if (props.verb && props.verb.changeTransitivity) { if (props.vps.verb && props.vps.verb.changeTransitivity) {
props.onChange(props.verb.changeTransitivity(t)); props.onChange({
...props.vps,
verb: props.vps.verb.changeTransitivity(t),
});
} }
} }
function handleChangeStatDyn(c: "stative" | "dynamic") { function handleChangeStatDyn(c: "stative" | "dynamic") {
if (props.verb && props.verb.changeStatDyn) { if (props.vps.verb && props.vps.verb.changeStatDyn) {
props.onChange(props.verb.changeStatDyn(c)); props.onChange({
...props.vps,
verb: props.vps.verb.changeStatDyn(c),
});
} }
} }
return <div className="mb-3"> 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"> {info && <div className="mt-3 mb-1 text-center">
<Hider <Hider
showing={showRootsAndStems} showing={showRootsAndStems}
@ -104,7 +79,7 @@ function VerbPicker(props: ({
</Hider> </Hider>
</div>} </div>}
<div className="d-flex flex-row justify-content-around flex-wrap" style={{ maxWidth: "400px", margin: "0 auto" }}> <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 <ButtonSelect
small small
options={[{ options={[{
@ -114,14 +89,14 @@ function VerbPicker(props: ({
label: "trans.", label: "trans.",
value: "transitive", value: "transitive",
}]} }]}
value={notInstransitive(props.verb.transitivity)} value={notInstransitive(props.vps.verb.transitivity)}
handleChange={handleChangeTransitivity} handleChange={handleChangeTransitivity}
/> />
</div>} </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 <ButtonSelect
small small
value={props.verb.voice} value={props.vps.verb.voice}
options={[{ options={[{
label: "Active", label: "Active",
value: "active", value: "active",
@ -132,7 +107,7 @@ function VerbPicker(props: ({
handleChange={onVoiceSelect} handleChange={onVoiceSelect}
/> />
</div>} </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 <ButtonSelect
small small
options={[{ options={[{
@ -142,7 +117,7 @@ function VerbPicker(props: ({
label: "dynamic", label: "dynamic",
value: "dynamic", value: "dynamic",
}]} }]}
value={props.verb.isCompound ? props.verb.isCompound : "stative"} value={props.vps.verb.isCompound ? props.vps.verb.isCompound : "stative"}
handleChange={handleChangeStatDyn} handleChange={handleChangeStatDyn}
/> />
</div>} </div>}

View File

@ -5,31 +5,34 @@ import * as T from "../../types";
import { getVerbInfo } from "../../lib/verb-info"; import { getVerbInfo } from "../../lib/verb-info";
import { isPerfectTense } from "../../lib/phrase-building/vp-tools"; 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); 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 ( if (
!oldVerbSelection || !os ||
oldVerbSelection.object === "none" || os.verb.object === "none" ||
typeof oldVerbSelection.object === "number" || typeof os.verb.object === "number" ||
oldVerbSelection.isCompound === "dynamic" || os.verb.isCompound === "dynamic" ||
(oldVerbSelection.object?.type === "noun" && oldVerbSelection.object.dynamicComplement) (os.verb.object?.type === "noun" && os.verb.object.dynamicComplement)
) return undefined; ) return undefined;
return oldVerbSelection.object; return os.verb.object;
} }
const transitivity: T.Transitivity = "grammaticallyTransitive" in info const transitivity: T.Transitivity = "grammaticallyTransitive" in info
? "transitive" ? "transitive"
: info.transitivity; : info.transitivity;
const object = (transitivity === "grammatically transitive") const object = (transitivity === "grammatically transitive")
? T.Person.ThirdPlurMale ? 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) ? makeNounSelection(info.objComplement.entry as T.NounEntry, true)
: (transitivity === "transitive" && oldVerbSelection?.voice !== "passive") : (transitivity === "transitive" && os?.verb.voice !== "passive")
? getTransObjFromOldVerbSelection() ? getTransObjFromos()
: "none"; : "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") const isCompound = ("stative" in info || info.type === "stative compound")
? "stative" ? "stative"
: info.type === "dynamic compound" : info.type === "dynamic compound"
@ -47,18 +50,20 @@ export function makeVerbSelection(verb: T.VerbEntry, changeSubject: (s: T.NPSele
tenseCategory: "basic" | "modal", tenseCategory: "basic" | "modal",
tense: T.VerbTense, tense: T.VerbTense,
} => { } => {
if (!oldVerbSelection) { if (!os) {
return { tense: "presentVerb", tenseCategory: "basic" }; return { tense: "presentVerb", tenseCategory: "basic" };
} }
if (oldVerbSelection.tenseCategory === "modal") { if (os.verb.tenseCategory === "modal") {
return { tenseCategory: "modal", tense: isPerfectTense(oldVerbSelection.tense) ? "presentVerb" : oldVerbSelection.tense }; return { tenseCategory: "modal", tense: isPerfectTense(os.verb.tense) ? "presentVerb" : os.verb.tense };
} }
if (oldVerbSelection.tenseCategory === "basic") { if (os.verb.tenseCategory === "basic") {
return { tenseCategory: "basic", tense: isPerfectTense(oldVerbSelection.tense) ? "presentVerb" : oldVerbSelection.tense }; 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 { return {
subject,
verb: {
type: "verb", type: "verb",
verb: verb, verb: verb,
dynAuxVerb, dynAuxVerb,
@ -67,9 +72,9 @@ export function makeVerbSelection(verb: T.VerbEntry, changeSubject: (s: T.NPSele
transitivity, transitivity,
isCompound, isCompound,
voice: transitivity === "transitive" voice: transitivity === "transitive"
? (oldVerbSelection?.voice || "active") ? (os?.verb.voice || "active")
: "active", : "active",
negative: oldVerbSelection ? oldVerbSelection.negative : false, negative: os ? os.verb.negative : false,
...("grammaticallyTransitive" in info) ? { ...("grammaticallyTransitive" in info) ? {
changeTransitivity: function(t) { changeTransitivity: function(t) {
return { return {
@ -102,5 +107,6 @@ export function makeVerbSelection(verb: T.VerbEntry, changeSubject: (s: T.NPSele
}; };
}, },
} : {}, } : {},
},
}; };
} }

View File

@ -27,7 +27,7 @@ import { renderEnglishVPBase } from "./english-vp-rendering";
// TODO: ISSUE GETTING SPLIT HEAD NOT MATCHING WITH FUTURE VERBS // 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 // Sentence Rules Logic
const isPast = isPastTense(VP.verb.tense); const isPast = isPastTense(VP.verb.tense);
const isTransitive = VP.object !== "none"; const isTransitive = VP.object !== "none";

View File

@ -15,7 +15,6 @@ import {
import { import {
getVerbInfo, getVerbInfo,
} from "./lib/verb-info"; } from "./lib/verb-info";
import { makeVerbSelection } from "./components/vp-explorer/verb-selection";
import ConjugationViewer from "./components/ConjugationViewer"; import ConjugationViewer from "./components/ConjugationViewer";
import InflectionsTable from "./components/InflectionsTable"; import InflectionsTable from "./components/InflectionsTable";
import Pashto from "./components/Pashto"; import Pashto from "./components/Pashto";
@ -164,7 +163,6 @@ export {
capitalizeFirstLetter, capitalizeFirstLetter,
psStringFromEntry, psStringFromEntry,
getLong, getLong,
makeVerbSelection,
useStickyState, useStickyState,
randomPerson, randomPerson,
isInvalidSubjObjCombo, isInvalidSubjObjCombo,

View File

@ -508,13 +508,6 @@ export type Words = {
adverbs: AdverbEntry[], 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<> // TODO: make this Rendered<VPSelection> with recursive Rendered<>
export type VPRendered = { export type VPRendered = {
type: "VPRendered", type: "VPRendered",
@ -543,6 +536,18 @@ export type NounNumber = "singular" | "plural";
export type PerfectTense = `${EquativeTense} perfect`; 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 = { export type VerbSelection = {
type: "verb", type: "verb",
verb: VerbEntry, verb: VerbEntry,