starting work on phrase builder

This commit is contained in:
lingdocs 2022-03-15 12:12:02 +04:00
parent 0553076bab
commit 30e5697452
13 changed files with 393 additions and 43 deletions

View File

@ -0,0 +1,91 @@
import Select from "react-select";
import {
makeVerbSelectOption,
zIndexProps,
} from "./np-picker/picker-tools";
import {
Types as T,
} from "@lingdocs/pashto-inflector";
const tenseOptions = [{
label: "present",
value: "present",
}, {
label: "subjunctive",
value: "subjunctive",
}];
function makeVerbSelection(verb: VerbEntry, oldVerbSelection?: VerbSelection): VerbSelection {
function getTransObjFromOldVerbSelection() {
if (!oldVerbSelection || oldVerbSelection.object === "none" || typeof oldVerbSelection.object === "number") return undefined;
return oldVerbSelection.object;
}
// TODO: more complex types and unchangeable dynamic compound objects
const verbType: "intrans" | "trans" | "gramm trans" = verb.entry.c?.includes("v. intrans.")
? "intrans"
: verb.entry.c?.includes("v. gramm. trans.")
? "gramm trans"
: "trans";
const object = verbType === "gramm trans"
? T.Person.ThirdPlurMale
: verbType === "trans"
? getTransObjFromOldVerbSelection()
: "none";
console.log({ verbType, object });
return {
type: "verb",
verb,
tense: oldVerbSelection ? oldVerbSelection.tense : "present",
object,
};
}
function VerbPicker({ onChange, verb, verbs }: { verbs: VerbEntry[], verb: VerbSelection | undefined, onChange: (p: VerbSelection) => void }) {
const options = verbs.map(makeVerbSelectOption)
function onEntrySelect({ value }: { label: string, value: string }) {
const v = verbs.find(v => v.entry.ts.toString() === value);
if (!v) {
console.error("entry not found");
return;
}
onChange(makeVerbSelection(v, verb));
}
function onTenseSelect({ value }: { label: string, value: "present" | "subjunctive" }) {
if (verb) {
console.log("changing to", value)
onChange({
...verb,
tense: value,
});
}
}
return <div style={{ maxWidth: "225px" }}>
<div>Verb:</div>
<Select
value={verb && verb.verb.entry.ts.toString()}
// @ts-ignore
onChange={onEntrySelect}
className="mb-2"
// @ts-ignore
options={options}
isSearchable
// // @ts-ignore
placeholder={verb ? options.find(o => o.value === (verb.verb.entry).ts.toString())?.label : "Select Verb..."}
{...zIndexProps}
/>
<div>Tense:</div>
<Select
value={verb && verb.tense}
// @ts-ignore
onChange={onTenseSelect}
className="mb-2"
// @ts-ignore
options={tenseOptions}
isSearchable
placeholder={verb ? tenseOptions.find(o => o.value === verb.tense)?.value : "Select Tense..."}
{...zIndexProps}
/>
</div>;
}
export default VerbPicker;

View File

@ -0,0 +1,69 @@
import Select from "react-select";
import {
makeSelectOption,
zIndexProps,
makeNounSelection,
} from "./picker-tools";
import {
ButtonSelect,
} from "@lingdocs/pashto-inflector";
function NPNounPicker({ onChange, noun, nouns }: { nouns: NounEntry[], noun: NounSelection | undefined, onChange: (p: NounSelection) => void }) {
const options = nouns.map(makeSelectOption)
function onEntrySelect({ value }: { label: string, value: string }) {
const entry = nouns.find(n => n.ts.toString() === value);
if (!entry) {
console.error("entry not found");
return;
}
onChange(makeNounSelection(entry));
}
return <div style={{ maxWidth: "225px" }}>
<Select
value={noun && noun.entry.ts.toString()}
// @ts-ignore
onChange={onEntrySelect}
className="mb-2"
// @ts-ignore
options={options}
isSearchable
// // @ts-ignore
placeholder={noun ? options.find(o => o.value === (noun.entry).ts.toString())?.label : "Select Noun..."}
{...zIndexProps}
/>
{noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>
{noun.changeGender ? <ButtonSelect
small
options={[
{ label: "Masc", value: "masc" },
{ label: "Fem", value: "fem" },
]}
value={noun.gender}
handleChange={(g) => {
if (!noun.changeGender) return;
onChange(noun.changeGender(g));
}}
/> : noun.gender === "masc" ? "Masc." : "Fem."}
</div>
<div>
{noun.changeNumber ? <ButtonSelect
small
options={[
{ label: "Sing.", value: "sing" },
{ label: "Plur.", value: "plur" },
]}
value={noun.number}
handleChange={(n) => {
if (!noun.changeNumber) return;
onChange(noun.changeNumber(n));
}}
/> : noun.number === "sing" ? "Sing." : "Plur."}
</div>
</div>}
</div>;
}
export default NPNounPicker;

View File

@ -0,0 +1,44 @@
import Select from "react-select";
import {
makeSelectOption,
zIndexProps,
} from "./picker-tools";
function makeParticipleSelection(verb: VerbEntry): ParticipleSelection {
return {
type: "participle",
verb,
};
}
function NPParticiplePicker({ onChange, participle, verbs }: { verbs: VerbEntry[], participle: ParticipleSelection | undefined, onChange: (p: ParticipleSelection) => void }) {
const options = verbs.map(makeSelectOption)
function onEntrySelect({ value }: { label: string, value: string }) {
const verb = verbs.find(v => v.entry.ts.toString() === value);
if (!verb) {
console.error("entry not found");
return;
}
onChange(makeParticipleSelection(verb));
}
return <div style={{ maxWidth: "225px" }}>
<Select
value={participle && participle.verb.entry.ts.toString()}
// @ts-ignore
onChange={onEntrySelect}
className="mb-2"
// @ts-ignore
options={options}
isSearchable
// // @ts-ignore
placeholder={participle ? options.find(o => o.value === (participle.verb.entry).ts.toString())?.label : "Select Participle..."}
{...zIndexProps}
/>
{participle && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>Masc.</div>
<div>Plur.</div>
</div>}
</div>;
}
export default NPParticiplePicker;

View File

@ -1,9 +1,12 @@
import PronounPicker from "./NPPronounPicker"; import PronounPicker from "./NPPronounPicker";
import { getEnglishPronoun } from "../../lib/english-pronoun-tools"; import NounPicker from "./NPNounPicker";
import ParticiplePicker from "./NPParticiplePicker";
// import { getEnglishPronoun } from "../../lib/english-pronoun-tools";
// import { ButtonSelect } from "@lingdocs/pashto-inflector"; // import { ButtonSelect } from "@lingdocs/pashto-inflector";
import { randomPerson } from "../../lib/np-tools"; import { randomPerson } from "../../lib/np-tools";
import { useState } from "react"; import { useState } from "react";
import { capitalizeFirstLetter } from "../../lib/text-tools"; import { nouns, verbs } from "../../words/words";
// import { capitalizeFirstLetter } from "../../lib/text-tools";
const npTypes: NPType[] = ["noun", "pronoun", "participle"]; const npTypes: NPType[] = ["noun", "pronoun", "participle"];
@ -19,17 +22,18 @@ function NPPicker({ np, onChange }: { onChange: (nps: NPSelection | undefined) =
const person = randomPerson(); const person = randomPerson();
const pronoun: PronounSelection = { const pronoun: PronounSelection = {
type: "pronoun", type: "pronoun",
e: capitalizeFirstLetter(getEnglishPronoun(person, "subject")),
person, person,
distance: "far", distance: "far",
}; };
setNpType(ntp);
onChange(pronoun); onChange(pronoun);
} else { } else {
onChange(undefined);
setNpType(ntp); setNpType(ntp);
} }
} }
return <div> return <div style={{ maxWidth: "300px"}}>
{!np ? {!npType ?
<div> <div>
{npTypes.map((npt) => ( {npTypes.map((npt) => (
<button <button
@ -42,12 +46,18 @@ function NPPicker({ np, onChange }: { onChange: (nps: NPSelection | undefined) =
</button> </button>
))} ))}
</div> </div>
: <div onClick={handleClear}>X</div>} : <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>}
{np && {np ?
(np.type === "pronoun" ((np.type === "pronoun"
? <PronounPicker pronoun={np} onChange={onChange} /> ? <PronounPicker pronoun={np} onChange={onChange} />
: <div>Not Implemented</div>) : np.type === "noun"
} ? <NounPicker nouns={nouns} noun={np} onChange={onChange} />
: <ParticiplePicker verbs={verbs} participle={np} onChange={onChange} />))
: (npType === "noun")
? <NounPicker nouns={nouns} noun={np} onChange={onChange} />
: (npType === "participle")
? <ParticiplePicker verbs={verbs} participle={np} onChange={onChange} />
: null}
</div>; </div>;
} }

View File

@ -2,8 +2,6 @@ import {
Types as T, Types as T,
ButtonSelect, ButtonSelect,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import { getEnglishPronoun } from "../../lib/english-pronoun-tools";
import { capitalizeFirstLetter } from "../../lib/text-tools";
import useStickyState from "../../useStickyState"; import useStickyState from "../../useStickyState";
const gColors = { const gColors = {
@ -60,7 +58,6 @@ function NPPronounPicker({ onChange, pronoun }: { pronoun: PronounSelection, onC
const person = pickerStateToPerson({ ...p, row, col }); const person = pickerStateToPerson({ ...p, row, col });
onChange({ onChange({
...pronoun, ...pronoun,
e: capitalizeFirstLetter(getEnglishPronoun(person, "subject")),
person, person,
}); });
} }
@ -68,7 +65,6 @@ function NPPronounPicker({ onChange, pronoun }: { pronoun: PronounSelection, onC
const person = pickerStateToPerson({ ...p, gender }); const person = pickerStateToPerson({ ...p, gender });
onChange({ onChange({
...pronoun, ...pronoun,
e: capitalizeFirstLetter(getEnglishPronoun(person, "subject")),
person, person,
}); });
} }

View File

@ -0,0 +1,74 @@
import {
isNounEntry,
isAdjectiveEntry,
isAdverbEntry,
isPluralNounEntry,
isMascNounEntry,
isUnisexNounEntry,
isVerbEntry,
} from "../../lib/type-predicates";
import {
getEnglishParticiple,
getEnglishVerb,
} from "../../lib/np-tools";
import {
getEnglishWord,
removeFVarients,
} from "@lingdocs/pashto-inflector";
export const zIndexProps = {
menuPortalTarget: document.body,
styles: { menuPortal: (base: any) => ({ ...base, zIndex: 9999 }) },
};
export function makeVerbSelectOption(e: VerbEntry): { value: string, label: string } {
const eng = getEnglishVerb(e.entry);
return {
label: `${e.entry.p} - ${removeFVarients(e.entry.f)} ${eng ? `(${eng})` : ""}`,
value: e.entry.ts.toString(),
};
}
export function makeSelectOption(e: VerbEntry | NounEntry | AdjectiveEntry | LocativeAdverbEntry): { value: string, label: string } {
const entry = "entry" in e ? e.entry : e;
const eng = (isVerbEntry(e))
? (getEnglishParticiple(e.entry))
: getEnglishWord(e);
const english = typeof eng === "string"
? eng
: !eng
? ""
: ("singular" in eng && eng.singular !== undefined)
? eng.singular
: eng.plural;
return {
label: `${entry.p} - ${removeFVarients(entry.f)} (${english})`,
value: entry.ts.toString(),
};
}
export function makeNounSelection(entry: NounEntry): NounSelection {
const number = isPluralNounEntry(entry) ? "plur" : "sing";
return {
type: "noun",
entry,
gender: isMascNounEntry(entry) ? "masc" : "fem",
number,
...isUnisexNounEntry(entry) ? {
changeGender: function(gender: "masc" | "fem"): NounSelection {
return {
...this,
gender,
};
},
} : {},
...number === "sing" ? {
changeNumber: function(number: "plur" | "sing"): NounSelection {
return {
...this,
number,
};
},
} : {},
};
}

View File

@ -0,0 +1,10 @@
import NPPicker from "../np-picker/NPPicker";
function ObjectDisplay({ object, onChange }: { object: Exclude<VerbObject, "none">, onChange: (o: NPSelection | undefined) => void }) {
return (typeof object === "number")
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker np={object} onChange={onChange} />;
}
export default ObjectDisplay;

View File

@ -0,0 +1,37 @@
import { useState } from "react";
import NPPicker from "../np-picker/NPPicker";
import VerbPicker from "../VerbPicker";
import ObjectDisplay from "./ObjectDisplay";
import { verbs } from "../../words/words";
export function PhraseBuilder() {
const [subject, setSubject] = useState<NPSelection | undefined>(undefined);
const [verb, setVerb] = useState<VerbSelection | undefined>(undefined);
function handleObjectChange(object: NPSelection | undefined) {
if (!verb) return;
if ((verb.object === "none") || (typeof verb.object === "number")) return;
setVerb({
...verb,
object,
});
}
console.log({ subject, verb });
return <div>
<div className="d-flex flex-row justify-content-between">
<div className="mr-2">
<div className="h5">Subject</div>
<NPPicker np={subject} onChange={setSubject} />
</div>
{verb && (verb.object !== "none") && <div className="mr-2">
<div className="h5">Object</div>
<ObjectDisplay object={verb.object} onChange={handleObjectChange} />
</div>}
<div>
<div className="h5">Verb</div>
<VerbPicker verbs={verbs} verb={verb} onChange={setVerb} />
</div>
</div>
</div>
}
export default PhraseBuilder;

View File

@ -87,7 +87,7 @@ import * as games from "!babel-loader!@lingdocs/mdx-loader!./games.mdx";
// @ts-ignore // @ts-ignore
import * as pronounPicker from "!babel-loader!@lingdocs/mdx-loader!./practice-tools/pronoun-picker.mdx"; import * as pronounPicker from "!babel-loader!@lingdocs/mdx-loader!./practice-tools/pronoun-picker.mdx";
// @ts-ignore // @ts-ignore
// import * as phraseBuilder from "!babel-loader!@lingdocs/mdx-loader!./practice-tools/phrase-builder.mdx"; import * as phraseBuilder from "!babel-loader!@lingdocs/mdx-loader!./practice-tools/phrase-builder.mdx";
const contentTree = [ const contentTree = [
{ {
@ -284,10 +284,10 @@ const contentTree = [
import: pronounPicker, import: pronounPicker,
slug: "pronoun-picker", slug: "pronoun-picker",
}, },
// { {
// import: phraseBuilder, import: phraseBuilder,
// slug: "phrase-builder", slug: "phrase-builder",
// }, },
], ],
}, },
]; ];

View File

@ -2,17 +2,8 @@
title: Phrase Builder title: Phrase Builder
--- ---
import NPPicker from "../../components/np-picker/NPPicker"; import PhraseBuilder from "../../components/phrase-builder/PhraseBuilder";
import { useState } from "react";
export function Display() { This is under construction... It's not quite working yet. 👷🚧
const [np, setNp] = useState(undefined);
return <div>
<NPPicker np={np} onChange={setNp} />
<pre>
{JSON.stringify(np, null, " ")}
</pre>
</div>
}
<Display /> <PhraseBuilder />

View File

@ -145,6 +145,20 @@ function getInf(infs: T.InflectorOutput, t: "plural" | "arabicPlural" | "inflect
return []; return [];
} }
export function getEnglishVerb(entry: T.DictionaryEntry): string {
if (!entry.ec) {
console.log("errored verb");
console.log(entry);
throw new Error("no english information for verb");
}
if (entry.ep) {
const ec = entry.ec.includes(",") ? parseEc(entry.ec) : entry.ec;
return `to ${ec} ${entry.ep}`;
}
const ec = parseEc(entry.ec);
return `to ${ec[0]}`;
}
export function getEnglishParticiple(entry: T.DictionaryEntry): string { export function getEnglishParticiple(entry: T.DictionaryEntry): string {
if (!entry.ec) { if (!entry.ec) {
console.log("errored participle"); console.log("errored participle");

37
src/types/gen-g.d.ts vendored
View File

@ -4,6 +4,28 @@
// type FemPlurNounEntry = ""; // can change nothing // type FemPlurNounEntry = ""; // can change nothing
// type UnisexNounEntry = ""; // can change number or gender // type UnisexNounEntry = ""; // can change number or gender
type VP = {
subject: NPSelection,
object: NPSelection,
verb: VerbSelection,
};
type VerbSelection = {
type: "verb",
verb: VerbEntry,
tense: "present" | "subjunctive",
object: VerbObject,
};
type VerbObject = // intransitive verb
"none" |
// transitive verb - object not selected yet
undefined |
// transitive verb - obect selected
NPSelection |
// grammatically transitive verb with unspoken 3rd pers masc plur entity
import("@lingdocs/pashto-inflector").Types.Person.ThirdPlurMale;
type NPSelection = NounSelection | PronounSelection | ParticipleSelection; type NPSelection = NounSelection | PronounSelection | ParticipleSelection;
type NPType = "noun" | "pronoun" | "participle"; type NPType = "noun" | "pronoun" | "participle";
@ -11,13 +33,7 @@ type NPType = "noun" | "pronoun" | "participle";
// TODO require/import Person and PsString // TODO require/import Person and PsString
type NounSelection = { type NounSelection = {
type: "noun", type: "noun",
ps: import("@lingdocs/pashto-inflector").Types.PsString, entry: NounEntry,
entry: import("@lingdocs/pashto-inflector").Types.DictionaryEntry,
// BETTER TO USE (or keep handy) FULL ENTRY FOR USE WITH INFLECTING
e: {
sing: string,
plur: string,
} | undefined,
gender: "masc" | "fem", gender: "masc" | "fem",
number: "sing" | "plur", number: "sing" | "plur",
// TODO: Implement // TODO: Implement
@ -33,17 +49,14 @@ type NounSelection = {
// take an argument for subject/object in rendering English // take an argument for subject/object in rendering English
type PronounSelection = { type PronounSelection = {
type: "pronoun", type: "pronoun",
e: string,
person: import("@lingdocs/pashto-inflector").Types.Person, person: import("@lingdocs/pashto-inflector").Types.Person,
distance: "near" | "far", distance: "near" | "far",
}; };
type ParticipleSelection = { type ParticipleSelection = {
type: "participle", type: "participle",
ps: import("@lingdocs/pashto-inflector").Types.PsString, verb: VerbEntry,
e: string | undefined, };
// entry in here
}
// not object // not object
// type Primitive = string | Function | number | boolean | Symbol | undefined | null; // type Primitive = string | Function | number | boolean | Symbol | undefined | null;

View File

@ -16,6 +16,7 @@
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noImplicitThis": true,
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true