abstracted proper entry picker component - ugly inside, nice on outside

This commit is contained in:
lingdocs 2022-04-04 16:23:01 +05:00
parent 03c7a8ba02
commit f1dd160b53
6 changed files with 102 additions and 54 deletions

View File

@ -0,0 +1,67 @@
import {
Types as T,
} from "@lingdocs/pashto-inflector";
import Select from "react-select";
import {
makeSelectOption,
makeVerbSelectOption,
zIndexProps,
} from "./np-picker/picker-tools";
function EntrySelect<E extends T.DictionaryEntry | VerbEntry>(props: {
entries: E[],
value: E | undefined,
onChange: (value: E | undefined) => void,
name: string | undefined,
isVerbSelect?: boolean,
}) {
const options = props.entries
.sort((a, b) => {
if ("entry" in a) {
return a.entry.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS")
}
return a.p.localeCompare("p" in b ? b.p : b.entry.p, "af-PS");
})
.map((e) => {
if ("entry" in e) {
return (props.isVerbSelect ? makeVerbSelectOption : makeSelectOption)(e);
}
return makeSelectOption(e);
});
function onSelect(v: { label: string, value: string } | null) {
if (!v) {
props.onChange(undefined);
return;
}
const s = props.entries.find(e => (
("entry" in e)
? e.entry.ts.toString() === v.value
: e.ts.toString() === v.value
));
if (!s) return;
props.onChange(s);
}
const selectedEntry: T.DictionaryEntry | undefined = !props.value
? undefined
: "entry" in props.value
? props.value.entry
: "p" in props.value
? props.value
: undefined;
const selected = !selectedEntry
? undefined
: options.find(o => (selectedEntry && (o.value === selectedEntry.ts.toString())));
return <div>
<Select
isSearchable={true}
value={selected}
onChange={onSelect}
className="mb-2"
options={options}
placeholder={props.name ? `Select ${props.name}...` : undefined}
{...zIndexProps}
/>
</div>
}
export default EntrySelect;

View File

@ -1,7 +1,6 @@
import Select from "react-select"; import Select from "react-select";
import { import {
makeNounSelection, makeNounSelection,
makeVerbSelectOption,
zIndexProps, zIndexProps,
} from "./np-picker/picker-tools"; } from "./np-picker/picker-tools";
import { import {
@ -10,6 +9,7 @@ import {
getVerbInfo, getVerbInfo,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import { isPerfectTense } from "../lib/phrase-building/vp-tools"; import { isPerfectTense } from "../lib/phrase-building/vp-tools";
import EntrySelect from "./EntrySelect";
// import { useState } from "react"; // import { useState } from "react";
const tenseOptions: { label: string | JSX.Element, value: VerbTense }[] = [{ const tenseOptions: { label: string | JSX.Element, value: VerbTense }[] = [{
@ -73,7 +73,7 @@ function VerbPicker({ onChange, subject, changeSubject, verb, verbs }: {
verbs: VerbEntry[], verbs: VerbEntry[],
verb: VerbSelection | undefined, verb: VerbSelection | undefined,
subject: NPSelection | undefined, subject: NPSelection | undefined,
onChange: (p: VerbSelection) => void, onChange: (p: VerbSelection | undefined) => void,
changeSubject: (p: NPSelection | undefined) => void, changeSubject: (p: NPSelection | undefined) => void,
}) { }) {
// const [filters, useFilters] = useState<Filters>({ // const [filters, useFilters] = useState<Filters>({
@ -83,12 +83,10 @@ function VerbPicker({ onChange, subject, changeSubject, verb, verbs }: {
// intransitive: true, // intransitive: true,
// grammaticallyTransitive: true, // grammaticallyTransitive: true,
// }); // });
const options = verbs.sort((a, b) => (a.entry.p.localeCompare(b.entry.p, "af-PS"))).map(makeVerbSelectOption); function onVerbSelect(v: VerbEntry | undefined) {
function onEntrySelect({ value }: { label: string, value: string }) { // TODO: what to do when clearing
const v = verbs.find(v => v.entry.ts.toString() === value);
if (!v) { if (!v) {
console.error("entry not found"); return onChange(v);
return;
} }
onChange(makeVerbSelection(v, changeSubject, verb)); onChange(makeVerbSelection(v, changeSubject, verb));
} }
@ -178,7 +176,13 @@ function VerbPicker({ onChange, subject, changeSubject, verb, verbs }: {
const tOptions = (verb?.tenseCategory === "perfect") ? perfectTenseOptions : tenseOptions; const tOptions = (verb?.tenseCategory === "perfect") ? perfectTenseOptions : tenseOptions;
return <div style={{ maxWidth: "225px", minWidth: "175px" }}> return <div style={{ maxWidth: "225px", minWidth: "175px" }}>
<div>Verb:</div> <div>Verb:</div>
<Select <EntrySelect
entries={verbs}
value={verb?.verb}
onChange={onVerbSelect}
name="Verb"
/>
{/* <Select
value={verb && verb.verb.entry.ts.toString()} value={verb && verb.verb.entry.ts.toString()}
// @ts-ignore // @ts-ignore
onChange={onEntrySelect} onChange={onEntrySelect}
@ -189,7 +193,7 @@ function VerbPicker({ onChange, subject, changeSubject, verb, verbs }: {
// // @ts-ignore // // @ts-ignore
placeholder={verb ? options.find(o => o.value === (verb.verb.entry).ts.toString())?.label : "Select Verb..."} placeholder={verb ? options.find(o => o.value === (verb.verb.entry).ts.toString())?.label : "Select Verb..."}
{...zIndexProps} {...zIndexProps}
/> /> */}
{verb && verb.changeTransitivity && <div className="text-center mt-3"> {verb && verb.changeTransitivity && <div className="text-center mt-3">
<ButtonSelect <ButtonSelect
small small

View File

@ -1,7 +1,4 @@
import Select from "react-select";
import { import {
makeSelectOption,
zIndexProps,
makeNounSelection, makeNounSelection,
} from "./picker-tools"; } from "./picker-tools";
import { import {
@ -11,6 +8,7 @@ import {
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import { useState } from "react"; import { useState } from "react";
import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates"; import { isFemNounEntry, isPattern1Entry, isPattern2Entry, isPattern3Entry, isPattern4Entry, isPattern5Entry, isPattern6FemEntry } from "../../lib/type-predicates";
import EntrySelect from "../EntrySelect";
const filterOptions = [ const filterOptions = [
{ {
@ -59,18 +57,15 @@ function nounFilter(p: FilterPattern | undefined) {
: () => true; : () => true;
} }
function NPNounPicker({ onChange, noun, nouns, clearButton }: { nouns: NounEntry[], noun: NounSelection | undefined, onChange: (p: NounSelection) => void, clearButton?: JSX.Element }) { function NPNounPicker({ onChange, noun, nouns, clearButton }: { nouns: NounEntry[], noun: NounSelection | undefined, onChange: (p: NounSelection | undefined) => void, clearButton?: JSX.Element }) {
const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined); const [patternFilter, setPatternFilter] = useState<FilterPattern | undefined>(undefined);
const [showFilter, setShowFilter] = useState<boolean>(false) const [showFilter, setShowFilter] = useState<boolean>(false)
const options = nouns const nounsFiltered = nouns
.filter(nounFilter(patternFilter)) .filter(nounFilter(patternFilter))
.sort((a, b) => (a.p.localeCompare(b.p, "af-PS"))) .sort((a, b) => (a.p.localeCompare(b.p, "af-PS")));
.map(makeSelectOption); function onEntrySelect(entry: NounEntry | undefined) {
function onEntrySelect({ value }: { label: string, value: string }) {
const entry = nouns.find(n => n.ts.toString() === value);
if (!entry) { if (!entry) {
console.error("entry not found"); return onChange(undefined);
return;
} }
onChange(makeNounSelection(entry)); onChange(makeNounSelection(entry));
} }
@ -101,17 +96,11 @@ function NPNounPicker({ onChange, noun, nouns, clearButton }: { nouns: NounEntry
/> />
</div>} </div>}
{!(noun && noun.dynamicComplement) ? <div> {!(noun && noun.dynamicComplement) ? <div>
<Select <EntrySelect
value={noun && noun.entry.ts.toString()} value={noun?.entry}
// @ts-ignore entries={nounsFiltered}
onChange={onEntrySelect} onChange={onEntrySelect}
className="mb-2" name="Noun"
// @ts-ignore
options={options}
isSearchable
// @ts-ignore
placeholder={noun ? options.find(o => o.value === (noun.entry).ts.toString())?.label : "Select Noun..."}
{...zIndexProps}
/> />
</div> : <div> </div> : <div>
{noun && <div> {noun && <div>

View File

@ -1,8 +1,4 @@
import Select from "react-select"; import EntrySelect from "../EntrySelect";
import {
makeSelectOption,
zIndexProps,
} from "./picker-tools";
function makeParticipleSelection(verb: VerbEntry): ParticipleSelection { function makeParticipleSelection(verb: VerbEntry): ParticipleSelection {
return { return {
@ -14,31 +10,23 @@ function makeParticipleSelection(verb: VerbEntry): ParticipleSelection {
function NPParticiplePicker({ onChange, participle, verbs, clearButton }: { function NPParticiplePicker({ onChange, participle, verbs, clearButton }: {
verbs: VerbEntry[], verbs: VerbEntry[],
participle: ParticipleSelection | undefined, participle: ParticipleSelection | undefined,
onChange: (p: ParticipleSelection) => void, onChange: (p: ParticipleSelection | undefined) => void,
clearButton: JSX.Element, clearButton: JSX.Element,
}) { }) {
const options = verbs.map(makeSelectOption) function onEntrySelect(entry: VerbEntry | undefined) {
function onEntrySelect({ value }: { label: string, value: string }) { if (!entry) {
const verb = verbs.find(v => v.entry.ts.toString() === value); onChange(undefined);
if (!verb) {
console.error("entry not found");
return; return;
} }
onChange(makeParticipleSelection(verb)); onChange(makeParticipleSelection(entry));
} }
return <div style={{ maxWidth: "225px" }}> return <div style={{ maxWidth: "225px" }}>
{clearButton} {clearButton}
<Select <EntrySelect
value={participle && participle.verb.entry.ts.toString()} value={participle?.verb}
// @ts-ignore entries={verbs}
onChange={onEntrySelect} onChange={onEntrySelect}
className="mb-2" name="Pariticple"
// @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"> {participle && <div className="my-2 d-flex flex-row justify-content-around align-items-center">
<div>Masc.</div> <div>Masc.</div>

View File

@ -27,7 +27,7 @@ export function makeVerbSelectOption(e: VerbEntry): { value: string, label: stri
}; };
} }
export function makeSelectOption(e: VerbEntry | NounEntry | AdjectiveEntry | LocativeAdverbEntry): { value: string, label: string } { export function makeSelectOption(e: T.DictionaryEntry | VerbEntry | NounEntry | AdjectiveEntry | LocativeAdverbEntry): { value: string, label: string } {
const entry = "entry" in e ? e.entry : e; const entry = "entry" in e ? e.entry : e;
const eng = (isVerbEntry(e)) const eng = (isVerbEntry(e))
? (getEnglishParticiple(e.entry)) ? (getEnglishParticiple(e.entry))

View File

@ -28,7 +28,7 @@ export function isNounOrAdjEntry(e: Entry): e is (NounEntry | AdjectiveEntry) {
return isNounEntry(e) || isAdjectiveEntry(e); return isNounEntry(e) || isAdjectiveEntry(e);
} }
export function isVerbEntry(e: Entry): e is VerbEntry { export function isVerbEntry(e: Entry | T.DictionaryEntry): e is VerbEntry {
return "entry" in e && !!e.entry.c?.startsWith("v."); return "entry" in e && !!e.entry.c?.startsWith("v.");
} }