This commit is contained in:
lingdocs 2022-07-07 13:58:39 -05:00
parent 8f1e283216
commit d24c6d8aba
6 changed files with 128 additions and 81 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@lingdocs/pashto-inflector", "name": "@lingdocs/pashto-inflector",
"version": "3.2.8", "version": "3.2.9",
"author": "lingdocs.com", "author": "lingdocs.com",
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations", "description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
"homepage": "https://verbs.lingdocs.com", "homepage": "https://verbs.lingdocs.com",

View File

@ -3,7 +3,7 @@ import classNames from "classnames";
import { import {
getEnglishFromRendered, getEnglishFromRendered,
} from "../../lib/phrase-building/np-tools"; } from "../../lib/phrase-building/np-tools";
import { getEnglishPersonInfo } from "../../library"; import { getEnglishPersonInfo, getEnglishParticipleInflection } from "../../lib/misc-helpers";
import { useState } from "react"; import { useState } from "react";
import { getLength } from "../../lib/p-text-helpers"; import { getLength } from "../../lib/p-text-helpers";
import { roleIcon } from "../vp-explorer/VPExplorerExplanationModal"; import { roleIcon } from "../vp-explorer/VPExplorerExplanationModal";
@ -101,8 +101,11 @@ function VerbSBlock({ opts, v, script }: {
<Border> <Border>
{getLength(v.ps, length)[0][script]} {getLength(v.ps, length)[0][script]}
</Border> </Border>
<div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"}</div> <div>{v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb"} aa</div>
<EnglishBelow>{getEnglishPersonInfo(v.person, "short")}</EnglishBelow> <EnglishBelow>{((v.type === "perfectParticipleBlock" ? "Past Partic." : "Verb")
? getEnglishParticipleInflection
: getEnglishPersonInfo
)(v.person, "short")}</EnglishBelow>
</div> </div>
} }

View File

@ -24,6 +24,7 @@ function NPPicker(props: {
entryFeeder: T.EntryFeeder, entryFeeder: T.EntryFeeder,
phraseIsComplete: boolean, phraseIsComplete: boolean,
isShrunk?: boolean, isShrunk?: boolean,
isRemoved?: boolean,
}) { }) {
if (props.is2ndPersonPicker && ((props.np?.selection.type !== "pronoun") || !isSecondPerson(props.np.selection.person))) { if (props.is2ndPersonPicker && ((props.np?.selection.type !== "pronoun") || !isSecondPerson(props.np.selection.person))) {
throw new Error("can't use 2ndPerson NPPicker without a pronoun"); throw new Error("can't use 2ndPerson NPPicker without a pronoun");
@ -106,7 +107,9 @@ function NPPicker(props: {
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button> ? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
: <div></div>; : <div></div>;
const possesiveLabel = props.np?.selection.type === "participle" ? "Subj/Obj" : "Possesor"; const possesiveLabel = props.np?.selection.type === "participle" ? "Subj/Obj" : "Possesor";
return <> return <div style={{
opacity: props.isRemoved ? 0.6 : 1,
}}>
<div className="d-flex flex-row justify-content-between"> <div className="d-flex flex-row justify-content-between">
<div></div> <div></div>
<div> <div>
@ -119,79 +122,79 @@ function NPPicker(props: {
</div> </div>
</div> </div>
<div style={{ minWidth: "9rem" }}> <div style={{ minWidth: "9rem" }}>
{!npType && <div className="text-center"> {!npType && <div className="text-center">
<div className="h6 mr-3"> <div className="h6 mr-3">
Choose NP Choose NP
</div>
{npTypes.map((npt) => <div key={npt} className="mb-2">
<button
key={npt}
type="button"
className="mr-2 btn btn-sm btn-outline-secondary"
onClick={() => handleNPTypeChange(npt)}
>
{npt}
</button>
</div>)}
</div>}
{(props.np && props.np.selection.type !== "pronoun" && (props.np.selection.possesor || addingPoss)) && <div className="mb-3" style={{
paddingLeft: "0.65rem",
borderLeft: "2px solid grey",
background: (props.np.selection.possesor?.shrunken && !props.isShrunk) ? shrunkenBackground : "inherit",
}}>
<div className="d-flex flex-row text-muted mb-2">
<div>{possesiveLabel}:</div>
{(props.np.selection.possesor && !props.isShrunk && props.phraseIsComplete) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}>
{!props.np.selection.possesor.shrunken ? "🪄" : "👶"}
</div>}
<div className="clickable ml-2" onClick={() => {
setAddingPoss(false);
handlePossesiveChange(undefined);
}}>
<i className="fas fa-trash" />
</div>
</div> </div>
{npTypes.map((npt) => <div key={npt} className="mb-2"> <NPPicker
<button phraseIsComplete={props.phraseIsComplete}
key={npt} onChange={handlePossesiveChange}
type="button" counterPart={undefined}
className="mr-2 btn btn-sm btn-outline-secondary" cantClear
onClick={() => handleNPTypeChange(npt)} np={props.np.selection.possesor ? props.np.selection.possesor.np : undefined}
> role="possesor"
{npt} opts={props.opts}
</button> entryFeeder={props.entryFeeder}
</div>)} />
</div>} </div>}
{(props.np && props.np.selection.type !== "pronoun" && (props.np.selection.possesor || addingPoss)) && <div className="mb-3" style={{ {(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div>
paddingLeft: "0.65rem", <span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ {possesiveLabel}</span>
borderLeft: "2px solid grey", </div>}
background: (props.np.selection.possesor?.shrunken && !props.isShrunk) ? shrunkenBackground : "inherit", {(npType === "pronoun" && props.np?.selection.type === "pronoun")
}}> ? <PronounPicker
<div className="d-flex flex-row text-muted mb-2"> role={props.role}
<div>{possesiveLabel}:</div> pronoun={props.np.selection}
{(props.np.selection.possesor && !props.isShrunk && props.phraseIsComplete) && <div className="clickable ml-3 mr-2" onClick={handleToggleShrunken}> onChange={(p) => onChange({ type: "NP", selection: p })}
{!props.np.selection.possesor.shrunken ? "🪄" : "👶"} is2ndPersonPicker={props.is2ndPersonPicker}
</div>} opts={props.opts}
<div className="clickable ml-2" onClick={() => { />
setAddingPoss(false); : npType === "noun"
handlePossesiveChange(undefined); ? <NounPicker
}}> phraseIsComplete={props.phraseIsComplete}
<i className="fas fa-trash" /> entryFeeder={props.entryFeeder}
</div> noun={(props.np && props.np.selection.type === "noun") ? props.np.selection : undefined}
</div> onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
<NPPicker opts={props.opts}
phraseIsComplete={props.phraseIsComplete} />
onChange={handlePossesiveChange} : npType === "participle"
counterPart={undefined} ? <ParticiplePicker
cantClear entryFeeder={props.entryFeeder.verbs}
np={props.np.selection.possesor ? props.np.selection.possesor.np : undefined} participle={(props.np && props.np.selection.type === "participle") ? props.np.selection : undefined}
role="possesor" onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
opts={props.opts} opts={props.opts}
entryFeeder={props.entryFeeder} />
/> : null
</div>} }
{(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div> </div>
<span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ {possesiveLabel}</span> </div>;
</div>}
{(npType === "pronoun" && props.np?.selection.type === "pronoun")
? <PronounPicker
role={props.role}
pronoun={props.np.selection}
onChange={(p) => onChange({ type: "NP", selection: p })}
is2ndPersonPicker={props.is2ndPersonPicker}
opts={props.opts}
/>
: npType === "noun"
? <NounPicker
phraseIsComplete={props.phraseIsComplete}
entryFeeder={props.entryFeeder}
noun={(props.np && props.np.selection.type === "noun") ? props.np.selection : undefined}
onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
opts={props.opts}
/>
: npType === "participle"
? <ParticiplePicker
entryFeeder={props.entryFeeder.verbs}
participle={(props.np && props.np.selection.type === "participle") ? props.np.selection : undefined}
onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
opts={props.opts}
/>
: null
}
</div>
</>;
} }
function ensureSingleShrink(old: T.NPSelection | undefined, s: T.NPSelection | undefined): T.NPSelection | undefined { function ensureSingleShrink(old: T.NPSelection | undefined, s: T.NPSelection | undefined): T.NPSelection | undefined {

View File

@ -95,9 +95,16 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
? <NPPicker ? <NPPicker
phraseIsComplete={phraseIsComplete} phraseIsComplete={phraseIsComplete}
heading={roles.king === "subject" heading={roles.king === "subject"
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div> ? <div className="h5 text-center">
Subj. <span onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>{roleIcon.king}</span>
{(rendered && rendered.whatsAdjustable !== "servant") &&
<span onClick={() => adjustVps({ type: "toggle king remove" })} className="mx-2 clickable">
{!VPS?.form.removeKing ? "🚫" : "🙈"}
</span>
}
</div>
: <div className="h5 text-center"> : <div className="h5 text-center">
Subject Subj.
{` `} {` `}
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span> <span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
{` `} {` `}
@ -117,6 +124,7 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
onChange={handleSubjectChange} onChange={handleSubjectChange}
opts={opts} opts={opts}
isShrunk={(servantIsShrunk && roles.servant === "subject")} isShrunk={(servantIsShrunk && roles.servant === "subject")}
isRemoved={roles.king === "subject" && VPS?.form.removeKing}
/> />
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none") : (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}> ? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
@ -130,9 +138,16 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
: <NPPicker : <NPPicker
phraseIsComplete={phraseIsComplete} phraseIsComplete={phraseIsComplete}
heading={roles.king === "object" heading={roles.king === "object"
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div> ? <div className="h5 text-center">
Obj. <span onClick={() => setShowingExplanation({ role: "king", item: "object" })}>{roleIcon.king}</span>
{(rendered && rendered.whatsAdjustable !== "servant") &&
<span onClick={() => adjustVps({ type: "toggle king remove" })} className="mx-2 clickable">
{!VPS?.form.removeKing ? "🚫" : "🙈"}
</span>
}
</div>
: <div className="h5 text-center"> : <div className="h5 text-center">
Object Obj.
{` `} {` `}
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span> <span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
{` `} {` `}
@ -141,6 +156,11 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
{!servantIsShrunk ? "🪄" : "👶"} {!servantIsShrunk ? "🪄" : "👶"}
</span> </span>
} }
{(rendered && rendered.whatsAdjustable !== "servant") &&
<span onClick={() => adjustVps({ type: "toggle king remove" })} className="mx-2 clickable">
{!VPS?.form.removeKing ? "🚫" : "🙈"}
</span>
}
</div>} </div>}
entryFeeder={entryFeeder} entryFeeder={entryFeeder}
role="object" role="object"
@ -149,6 +169,7 @@ function VPPicker({ opts, vps, onChange, entryFeeder }: {
onChange={handleObjectChange} onChange={handleObjectChange}
opts={opts} opts={opts}
isShrunk={(servantIsShrunk && roles.servant === "object")} isShrunk={(servantIsShrunk && roles.servant === "object")}
isRemoved={roles.king === "object" && VPS?.form.removeKing}
/>} />}
</div> </div>
: null} : null}

View File

@ -53,6 +53,8 @@ export type VpsReducerAction = {
} | { } | {
type: "toggle servant shrink", type: "toggle servant shrink",
} | { } | {
type: "toggle king remove",
} |{
type: "set verb", type: "set verb",
payload: T.VerbEntry, payload: T.VerbEntry,
} | { } | {
@ -251,6 +253,15 @@ export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, se
}, },
}; };
} }
if (action.type === "toggle king remove") {
return {
...vps,
form: {
...vps.form,
removeKing: !vps.form.removeKing,
},
};
}
if (action.type === "set verb") { if (action.type === "set verb") {
return makeVPSelectionState(action.payload, vps); return makeVPSelectionState(action.payload, vps);
} }

View File

@ -163,6 +163,15 @@ export function getEnglishPersonInfo(person: T.Person, version?: "short" | "long
return `${p} ${n}. ${g}.`; return `${p} ${n}. ${g}.`;
} }
export function getEnglishParticipleInflection(person: T.Person, version?: "short" | "long"): string {
const number = personIsPlural(person) ? "plural" : "singular";
const n = version === "short"
? (number === "plural" ? "plur." : "sing.") : number;
const gender = personGender(person);
const g = gender;
return `${g}. ${n}`;
}
export function randomNumber(minInclusive: number, maxExclusive: number): number { export function randomNumber(minInclusive: number, maxExclusive: number): number {
return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive); return Math.floor(Math.random() * (maxExclusive - minInclusive) + minInclusive);
} }