refactoring to allow for dictionary lookup in phrase builder

This commit is contained in:
lingdocs 2022-04-08 11:54:47 +05:00
parent 8191b48f05
commit 759df4811e
6 changed files with 195 additions and 120 deletions

View File

@ -13,7 +13,7 @@ function EntrySelect<E extends T.DictionaryEntry | VerbEntry>(props: ({
entries: E[]
} | {
searchF: (search: string) => E[],
getByTs: (ts: number) => E,
getByTs: (ts: number) => E | undefined,
}) & {
value: E | undefined,
onChange: (value: E | undefined) => void,

View File

@ -6,83 +6,93 @@ import {
InlinePs,
Types as T,
} from "@lingdocs/pashto-inflector";
import { useState } from "react";
import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates";
// import { useState } from "react";
// import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates";
import EntrySelect from "../EntrySelect";
const filterOptions = [
{
label: "1",
value: "1",
},
{
label: "2",
value: "2",
},
{
label: "3",
value: "3",
},
{
label: "4",
value: "4",
},
{
label: "5",
value: "5",
},
{
label: "6",
value: "6",
},
];
// const filterOptions = [
// {
// label: "1",
// value: "1",
// },
// {
// label: "2",
// value: "2",
// },
// {
// label: "3",
// value: "3",
// },
// {
// label: "4",
// value: "4",
// },
// {
// label: "5",
// value: "5",
// },
// {
// label: "6",
// value: "6",
// },
// ];
type FilterPattern = "1" | "2" | "3" | "4" | "5" | "6";
// type FilterPattern = "1" | "2" | "3" | "4" | "5" | "6";
function nounFilter(p: FilterPattern | undefined) {
return p === undefined
? () => true
: (p === "1")
? isPattern1Entry
: (p === "2")
? isPattern2Entry
: (p === "3")
? isPattern3Entry
: (p === "4")
? isPattern4Entry
: (p === "5")
? isPattern5Entry
: (p === "6")
? (n: NounEntry) => (isFemNounEntry(n) && isPattern6FemEntry(n))
: () => true;
}
// function nounFilter(p: FilterPattern | undefined) {
// return p === undefined
// ? () => true
// : (p === "1")
// ? isPattern1Entry
// : (p === "2")
// ? isPattern2Entry
// : (p === "3")
// ? isPattern3Entry
// : (p === "4")
// ? isPattern4Entry
// : (p === "5")
// ? isPattern5Entry
// : (p === "6")
// ? (n: NounEntry) => (isFemNounEntry(n) && isPattern6FemEntry(n))
// : () => true;
// }
function NPNounPicker({ onChange, noun, nouns, clearButton, opts }: { nouns: NounEntry[], noun: NounSelection | undefined, onChange: (p: NounSelection | undefined) => void, clearButton?: JSX.Element, opts: T.TextOptions }) {
const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
const [showFilter, setShowFilter] = useState<boolean>(false)
const nounsFiltered = nouns
.filter(nounFilter(patternFilter))
.sort((a, b) => (a.p.localeCompare(b.p, "af-PS")));
function NPNounPicker(props: ({
nouns: NounEntry[],
} | {
nouns: (s: string) => NounEntry[],
getNounByTs: (ts: number) => NounEntry | undefined;
}) & {
noun: NounSelection | undefined,
onChange: (p: NounSelection | undefined) => void,
clearButton?: JSX.Element,
opts: T.TextOptions,
}) {
// const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
// const [showFilter, setShowFilter] = useState<boolean>(false)
// const nounsFiltered = props.nouns
// .filter(nounFilter(patternFilter))
// .sort((a, b) => (a.p.localeCompare(b.p, "af-PS")));
function onEntrySelect(entry: NounEntry | undefined) {
if (!entry) {
return onChange(undefined);
return props.onChange(undefined);
}
onChange(makeNounSelection(entry));
}
function handleFilterClose() {
setPatternFilter(undefined);
setShowFilter(false);
props.onChange(makeNounSelection(entry));
}
// function handleFilterClose() {
// setPatternFilter(undefined);
// setShowFilter(false);
// }
return <div style={{ maxWidth: "225px", minWidth: "125px" }}>
<div className="d-flex flex-row justify-content-between">
{clearButton}
{(!showFilter && !(noun?.dynamicComplement)) && <div className="text-right">
<div className="d-flex flex-row justify-content-left">
{props.clearButton}
{/* {(!showFilter && !(noun?.dynamicComplement)) && <div className="text-right">
<button className="btn btn-sm btn-light mb-2 text-small" onClick={() => setShowFilter(true)}>
<i className="fas fa-filter fa-xs" />
</button>
</div>}
</div>} */}
</div>
{showFilter && <div className="mb-2 text-center">
{/* {showFilter && <div className="mb-2 text-center">
<div className="d-flex flex-row justify-content-between">
<div className="text-small mb-1">Filter by inflection pattern</div>
<div className="clickable" onClick={handleFilterClose}>X</div>
@ -94,54 +104,59 @@ function NPNounPicker({ onChange, noun, nouns, clearButton, opts }: { nouns: Nou
// @ts-ignore
handleChange={setPatternFilter}
/>
</div>}
{!(noun && noun.dynamicComplement) ? <div>
</div>} */}
{!(props.noun && props.noun.dynamicComplement) ? <div>
<EntrySelect
value={noun?.entry}
entries={nounsFiltered}
value={props.noun?.entry}
{..."getNounByTs" in props ? {
getByTs: props.getNounByTs,
searchF: props.nouns
} : {
entries: props.nouns,
}}
onChange={onEntrySelect}
name="Noun"
opts={opts}
opts={props.opts}
/>
</div> : <div>
{noun && <div>
{props.noun && <div>
<div className="mb-2">Included in Dyn. Compound:</div>
<div className="mb-3 text-center">
<InlinePs opts={opts}>
{{ p: noun.entry.p, f: noun.entry.f }}
<InlinePs opts={props.opts}>
{{ p: props.noun.entry.p, f: props.noun.entry.f }}
</InlinePs>
<div className="text-muted">{noun.entry.e}</div>
<div className="text-muted">{props.noun.entry.e}</div>
</div>
</div>}
</div>}
{noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
{props.noun && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>
{noun.changeGender ? <ButtonSelect
{props.noun.changeGender ? <ButtonSelect
small
options={[
{ label: "Masc", value: "masc" },
{ label: "Fem", value: "fem" },
]}
value={noun.gender}
value={props.noun.gender}
handleChange={(g) => {
if (!noun.changeGender) return;
onChange(noun.changeGender(g));
if (!props.noun || !props.noun.changeGender) return;
props.onChange(props.noun.changeGender(g));
}}
/> : noun.gender === "masc" ? "Masc." : "Fem."}
/> : props.noun.gender === "masc" ? "Masc." : "Fem."}
</div>
<div>
{noun.changeNumber ? <ButtonSelect
{props.noun.changeNumber ? <ButtonSelect
small
options={[
{ label: "Sing.", value: "singular" },
{ label: "Plur.", value: "plural" },
]}
value={noun.number}
value={props.noun.number}
handleChange={(n) => {
if (!noun.changeNumber) return;
onChange(noun.changeNumber(n));
if (!props.noun || !props.noun.changeNumber) return;
props.onChange(props.noun.changeNumber(n));
}}
/> : noun.number === "singular" ? "Sing." : "Plur."}
/> : props.noun.number === "singular" ? "Sing." : "Plur."}
</div>
</div>}
</div>;

View File

@ -10,8 +10,12 @@ function makeParticipleSelection(verb: VerbEntry): ParticipleSelection {
};
}
function NPParticiplePicker({ onChange, participle, verbs, clearButton, opts }: {
function NPParticiplePicker(props: ({
verbs: VerbEntry[],
} | {
verbs: (s: string) => VerbEntry[],
getVerbByTs: (ts: number) => VerbEntry | undefined;
}) & {
participle: ParticipleSelection | undefined,
onChange: (p: ParticipleSelection | undefined) => void,
clearButton: JSX.Element,
@ -19,21 +23,26 @@ function NPParticiplePicker({ onChange, participle, verbs, clearButton, opts }:
}) {
function onEntrySelect(entry: VerbEntry | undefined) {
if (!entry) {
onChange(undefined);
props.onChange(undefined);
return;
}
onChange(makeParticipleSelection(entry));
props.onChange(makeParticipleSelection(entry));
}
return <div style={{ maxWidth: "225px" }}>
{clearButton}
{props.clearButton}
<EntrySelect
value={participle?.verb}
entries={verbs}
value={props.participle?.verb}
{..."getVerbByTs" in props ? {
getByTs: props.getVerbByTs,
searchF: props.verbs,
} : {
entries: props.verbs,
}}
onChange={onEntrySelect}
name="Pariticple"
opts={opts}
opts={props.opts}
/>
{participle && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
{props.participle && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>Masc.</div>
<div>Plur.</div>
</div>}

View File

@ -5,7 +5,6 @@ import ParticiplePicker from "./NPParticiplePicker";
// import { ButtonSelect } from "@lingdocs/pashto-inflector";
import { randomPerson } from "../../lib/np-tools";
import { useState, useEffect } from "react";
import { nouns, verbs } from "../../words/words";
import {
Types as T,
} from "@lingdocs/pashto-inflector";
@ -13,38 +12,46 @@ import {
const npTypes: NPType[] = ["pronoun", "noun", "participle"];
function NPPicker({ np, onChange, counterPart, asObject, opts }: {
function NPPicker(props: {
onChange: (nps: NPSelection | undefined) => void,
np: NPSelection | undefined,
counterPart: NPSelection | VerbObject | undefined,
asObject?: boolean,
opts: T.TextOptions,
}) {
const [npType, setNpType] = useState<NPType | undefined>(np ? np.type : undefined);
} & ({
nouns: (s: string) => NounEntry[],
verbs: (s: string) => VerbEntry[],
getNounByTs: (ts: number) => NounEntry | undefined,
getVerbByTs: (ts: number) => VerbEntry | undefined,
} | {
nouns: NounEntry[],
verbs: VerbEntry[],
})) {
const [npType, setNpType] = useState<NPType | undefined>(props.np ? props.np.type : undefined);
useEffect(() => {
setNpType(np ? np.type : undefined);
}, [np]);
setNpType(props.np ? props.np.type : undefined);
}, [props.np]);
function handleClear() {
if (np && np.type === "noun" && np.dynamicComplement) return;
if (props.np && props.np.type === "noun" && props.np.dynamicComplement) return;
setNpType(undefined);
onChange(undefined);
props.onChange(undefined);
}
function handleNPTypeChange(ntp: NPType) {
if (ntp === "pronoun") {
const person = randomPerson({ counterPart });
const person = randomPerson({ counterPart: props.counterPart });
const pronoun: PronounSelection = {
type: "pronoun",
person,
distance: "far",
};
setNpType(ntp);
onChange(pronoun);
props.onChange(pronoun);
} else {
onChange(undefined);
props.onChange(undefined);
setNpType(ntp);
}
}
const isDynamicComplement = np && np.type === "noun" && np.dynamicComplement;
const isDynamicComplement = props.np && props.np.type === "noun" && props.np.dynamicComplement;
const clearButton = <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>;
return <div>
{!npType && <div className="text-center mt-3">
@ -62,29 +69,39 @@ function NPPicker({ np, onChange, counterPart, asObject, opts }: {
</button>
</div>)}
</div>}
{(npType === "pronoun" && np?.type === "pronoun")
{(npType === "pronoun" && props.np?.type === "pronoun")
? <PronounPicker
asObject={asObject}
pronoun={np}
onChange={onChange}
asObject={props.asObject}
pronoun={props.np}
onChange={props.onChange}
clearButton={clearButton}
opts={opts}
opts={props.opts}
/>
: npType === "noun"
? <NounPicker
nouns={nouns}
noun={(np && np.type === "noun") ? np : undefined}
onChange={onChange}
{..."getNounByTs" in props ? {
nouns: props.nouns,
getNounByTs: props.getNounByTs,
} : {
nouns: props.nouns,
}}
noun={(props.np && props.np.type === "noun") ? props.np : undefined}
onChange={props.onChange}
clearButton={!isDynamicComplement ? clearButton : undefined}
opts={opts}
opts={props.opts}
/>
: npType === "participle"
? <ParticiplePicker
verbs={verbs}
participle={(np && np.type === "participle") ? np : undefined}
onChange={onChange}
{..."getVerbByTs" in props ? {
verbs: props.verbs,
getVerbByTs: props.getVerbByTs,
} : {
verbs: props.verbs,
}}
participle={(props.np && props.np.type === "participle") ? props.np : undefined}
onChange={props.onChange}
clearButton={clearButton}
opts={opts}
opts={props.opts}
/>
: null
}

View File

@ -33,8 +33,16 @@ const servantEmoji = "🙇‍♂️";
// TODO: error handling on error with rendering etc
export function PhraseBuilder(props: {
verb?: VerbEntry,
opts?: T.TextOptions,
}) {
opts: T.TextOptions,
} & ({
nouns: NounEntry[],
verbs: VerbEntry[],
} | {
nouns: (s: string) => NounEntry[],
verbs: (s: string) => VerbEntry[],
getNounByTs: (ts: number) => NounEntry | undefined,
getVerbByTs: (ts: number) => VerbEntry | undefined,
})) {
const [subject, setSubject] = useStickyState<NPSelection | undefined>(undefined, "subjectNPSelection");
const [mode, setMode] = useStickyState<"charts" | "phrases">("phrases", "verbExplorerMode");
const passedVerb = props.verb;
@ -101,6 +109,15 @@ export function PhraseBuilder(props: {
<div className="my-2">
<div className="h5 text-center">Subject {showRole(VPRendered, "subject")}</div>
<NPPicker
{..."getNounByTs" in props ? {
getNounByTs: props.getNounByTs,
getVerbByTs: props.getVerbByTs,
nouns: props.nouns,
verbs: props.verbs,
} : {
nouns: props.nouns,
verbs: props.verbs,
}}
np={subject}
counterPart={verb ? verb.object : undefined}
onChange={handleSubjectChange}
@ -112,6 +129,15 @@ export function PhraseBuilder(props: {
{(typeof verb.object === "number")
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker
{..."getNounByTs" in props ? {
getNounByTs: props.getNounByTs,
getVerbByTs: props.getVerbByTs,
nouns: props.nouns,
verbs: props.verbs,
} : {
nouns: props.nouns,
verbs: props.verbs,
}}
asObject
np={verb.object}
counterPart={subject}

View File

@ -4,5 +4,13 @@ fullWidth: true
---
import PhraseBuilder from "../../components/phrase-builder/PhraseBuilder";
import {
defaultTextOptions,
} from "@lingdocs/pashto-inflector";
import { nouns, verbs } from "../../words/words";
<PhraseBuilder />
<PhraseBuilder
opts={defaultTextOptions}
nouns={nouns}
verbs={verbs}
/>