AP and NP block diagrams working

This commit is contained in:
lingdocs 2022-06-11 11:25:28 -04:00
parent f4dc09e941
commit 2ba678b1ff
10 changed files with 238 additions and 308 deletions

View File

@ -6,7 +6,7 @@
"@formkit/auto-animate": "^1.0.0-beta.1",
"@fortawesome/fontawesome-free": "^5.15.4",
"@lingdocs/lingdocs-main": "^0.3.1",
"@lingdocs/pashto-inflector": "^2.6.8",
"@lingdocs/pashto-inflector": "^2.8.2",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",

View File

@ -0,0 +1,45 @@
import {
Types as T,
Examples,
renderEP,
compileEP,
} from "@lingdocs/pashto-inflector";
import { completeEPSelection } from "@lingdocs/pashto-inflector/dist/lib/phrase-building/render-ep";
import { useState } from "react";
// import EPBlocks from "./EPBlocks";
function getShort<X>(i: T.SingleOrLengthOpts<X>): X {
if ("long" in i) {
return i.long;
}
return i;
}
function EditableExample({ children: eps, opts }: { children: T.EPSelectionState, opts: T.TextOptions }) {
const [mode, setMode] = useState<"example" | "blocks">("example");
const EPS = completeEPSelection(eps);
if (!EPS) {
return <div>Error: Invalid/incomplete Phrase</div>;
}
const rendered = renderEP(EPS);
const compiled = compileEP(rendered);
const text: T.PsString = {
...getShort(compiled.ps)[0],
e: compiled.e ? compiled.e.join(" / ") : undefined,
};
return <div>
<div className="d-flex flex-row justify-content-beginning">
{mode === "example" ? <div className="clickable" onClick={() => setMode("blocks")}>
<i className="fas fa-cubes" />
</div> : <div className="clickable" onClick={() => setMode("example")}>
<i className="fas fa-align-left" />
</div>}
</div>
{/* {mode === "example"
? <Examples opts={opts}>{[text]}</Examples>
: <EPBlocks opts={opts}>{rendered}</EPBlocks>} */}
</div>;
}
export default EditableExample;

View File

@ -16,10 +16,17 @@ export function EditIcon() {
return <i className="fas fa-edit" />;
}
function selectionToBlock(s: T.NPSelection | T.APSelection): { type: "NP", block: T.NPSelection | undefined } | { type: "AP", block: T.APSelection | undefined } {
return (s.type === "AP")
? { type: "AP", block: s }
: { type: "NP", block: s };
}
function EditablePhraseDiagram({ opts, children }: {
opts: T.TextOptions,
children: BlockInput[],
children: (T.NPSelection | T.APSelection)[],
}) {
console.log({ aa: children[0] })
const block = children[0];
const parent = useRef<HTMLDivElement>(null);
useEffect(() => {
@ -32,8 +39,7 @@ function EditablePhraseDiagram({ opts, children }: {
} | {
type: "AP",
block: T.APSelection | undefined,
}>(block);
console.log({ block });
}>(selectionToBlock(block));
if (children.length === 0) return null;
function handleNPChange(np: T.NPSelection | undefined) {
setEdited({ type: "NP", block: np });
@ -42,7 +48,7 @@ function EditablePhraseDiagram({ opts, children }: {
setEdited({ type: "AP", block: ap });
}
function handleReset() {
setEdited(block);
setEdited(selectionToBlock(block));
setEditing(false);
}
return <div>
@ -76,7 +82,7 @@ function EditablePhraseDiagram({ opts, children }: {
</div>}
{edited.block
&& <PhraseDiagram opts={opts}>
{[edited] as BlockInput[]}
{edited.block}
</PhraseDiagram>}
</div>
</div>;

View File

@ -1,182 +1,31 @@
import {
renderNPSelection,
Types as T,
getEnglishFromRendered,
renderAPSelection,
NPBlock,
APBlock,
} from "@lingdocs/pashto-inflector";
import classNames from "classnames";
function PhraseDiagram({ opts, children }: {
opts: T.TextOptions,
children: BlockInput[]
children: T.NPSelection | T.APSelection,
}) {
try {
const rendered = children.type === "AP"
? renderAPSelection(children)
: renderNPSelection(children, false, false, "subject", "none");
return <div>
<div className="d-flex flex-row justify-content-center" style={{ maxWidth: "100%" }}>
{children.map((block) => (
<Block key={Math.random()} opts={opts}>{block}</Block>
))}
{rendered.type === "NP"
? <NPBlock opts={opts}>{rendered}</NPBlock>
: <APBlock opts={opts}>{rendered}</APBlock>}
</div>
</div>;
} catch(e) {
console.log(e);
return <div>ERROR</div>;
}
}
function Block({ opts, children }: {
opts: T.TextOptions,
children: BlockInput,
}) {
if (children.type === "NP") {
const rendered = renderNPSelection(children.block, false, false, "subject", "none");
const english = getEnglishFromRendered(rendered)
return <div className="text-center mb-2">
<NP opts={opts} english={english}>{rendered}</NP>
</div>;
}
const rendered = renderAPSelection(children.block);
const english = getEnglishFromRendered(rendered)
return <div className="text-center mb-2">
<AP opts={opts} english={english}>{rendered}</AP>
</div>;
}
function AP({ opts, children, english }: {
opts: T.TextOptions,
children: T.Rendered<T.APSelection>,
english?: string,
}) {
const ap = children;
if (ap.type === "adverb") {
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "1rem",
textAlign: "center",
}}
>
{ap.ps[0].f}
</div>
<div>AP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>;
}
return <div>
<div className="text-center">Sandwich 🥪</div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center")}
style={{
border: "2px solid black",
padding: "0.75rem 0.5rem 0.25rem 0.5rem",
textAlign: "center",
}}
>
<div className="d-flex flex-row justify-content-between align-items-end">
<Possesors opts={opts}>{ap.inside.type !== "pronoun" ? ap.inside.possesor : undefined}</Possesors>
<div className="mr-2 ml-1 mb-1"><strong>{ap.before ? ap.before.f : ""}</strong></div>
<div>
<NP opts={opts} inside>{ap.inside}</NP>
</div>
<div className="ml-2 mr-1 mb-1"><strong>{ap.after ? ap.after.f : ""}</strong></div>
</div>
</div>
<div>AP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>;
}
function NP({ opts, children, inside, english }: {
opts: T.TextOptions,
children: T.Rendered<T.NPSelection>,
inside?: boolean,
english?: string,
}) {
const np = children;
const hasPossesor = !!(np.type !== "pronoun" && np.possesor);
return <div>
<div
className={classNames("d-flex flex-row justify-content-center align-items-center", { "pt-2": !inside && hasPossesor })}
style={{
border: "2px solid black",
padding: inside ? "0.3rem" : hasPossesor ? "0.5rem 1rem 0.25rem 1rem" : "1rem",
textAlign: "center",
}}
>
{!inside && <Possesors opts={opts}>{np.type !== "pronoun" ? np.possesor : undefined}</Possesors>}
<Adjectives opts={opts}>{np.adjectives}</Adjectives>
<div> {np.ps[0].f}</div>
</div>
<div className={inside ? "small" : ""}>NP</div>
{english && <div className="small text-muted text-center" style={{
// TODO: find a better way to keep this limited to the width of the div above
// don't let this make the div above expand
margin: "0 auto",
maxWidth: "300px",
}}>{english}</div>}
</div>
}
function Possesors({ opts, children }: {
opts: T.TextOptions,
children: { shrunken: boolean, np: T.Rendered<T.NPSelection> } | undefined,
}) {
if (!children) {
return null;
}
const contraction = checkForContraction(children.np);
return <div className="d-flex flex-row mr-1 align-items-end" style={{
marginBottom: "0.5rem",
borderBottom: "1px solid grey",
}}>
{children.np.type !== "pronoun" && <Possesors opts={opts}>{children.np.possesor}</Possesors>}
<div>
{contraction && <div className="mb-1">({contraction})</div>}
<div className={classNames("d-flex", "flex-row", "align-items-center", { "text-muted": contraction })}>
<div className="mr-1 pb-2">du</div>
<div>
<NP opts={opts} inside>{children.np}</NP>
</div>
</div>
</div>
</div>
}
function checkForContraction(np: T.Rendered<T.NPSelection>): string | undefined {
if (np.type !== "pronoun") return undefined;
if (np.person === T.Person.FirstSingMale || np.person === T.Person.FirstSingFemale) {
return "zmaa"
}
if (np.person === T.Person.SecondSingMale || np.person === T.Person.SecondSingFemale) {
return "staa"
}
if (np.person === T.Person.FirstPlurMale || np.person === T.Person.FirstPlurFemale) {
return "zmoonG"
}
if (np.person === T.Person.SecondPlurMale || np.person === T.Person.SecondPlurFemale) {
return "staaso"
}
return undefined;
}
function Adjectives({ opts, children }: {
opts: T.TextOptions,
children: T.Rendered<T.AdjectiveSelection>[] | undefined,
}) {
if (!children) {
return null;
}
return <em className="mr-1">
{children.map(a => a.ps[0].f).join(" ")}{` `}
</em>
}
export default PhraseDiagram;

View File

@ -28,7 +28,7 @@ An adverb is a word or expression that modifies the time, manner, place, etc. of
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "adverb",
entry: {"ts":1527815160,"i":2394,"p":"پرون","f":"paroon","g":"paroon","e":"yesterday","c":"adv."},
},
@ -38,7 +38,7 @@ An adverb is a word or expression that modifies the time, manner, place, etc. of
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "adverb",
entry: {"ts":1527819967,"i":5428,"p":"خامخا","f":"khaamakhaa","g":"khaamakhaa","e":"definitely, for sure, whether someone wants or not, willy-nilly (this last use more in Urdu)","c":"adv."},
},
@ -54,12 +54,14 @@ These "sandwiches" are also used as an adverb to give more information for the p
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "sandwich",
before: { p: "په", f: "pu" },
after: { p: "کې", f: "ke" },
e: "in",
inside: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812828,"i":10539,"p":"کور","f":"kor","g":"kor","e":"house, home","c":"n. m."},
gender: "masc",
@ -71,6 +73,7 @@ These "sandwiches" are also used as an adverb to give more information for the p
},
},
},
},
]}</EditablePhraseDiagram>
Because the inside of a sandwich is an <Link to="/phrase-structure/np/">NP</Link> we can also spice it up by adding adjectives.
@ -78,12 +81,14 @@ Because the inside of a sandwich is an <Link to="/phrase-structure/np/">NP</Link
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "sandwich",
before: { p: "په", f: "pu" },
after: { p: "کې", f: "ke" },
e: "in",
inside: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812828,"i":10539,"p":"کور","f":"kor","g":"kor","e":"house, home","c":"n. m."},
gender: "masc",
@ -98,6 +103,7 @@ Because the inside of a sandwich is an <Link to="/phrase-structure/np/">NP</Link
},
},
},
},
]}</EditablePhraseDiagram>
We can also add a possesor it hangs outside of the sandwich. All together it's still all considered one AP block though.
@ -105,12 +111,14 @@ We can also add a possesor it hangs outside of the sandwich. All together it's s
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "sandwich",
before: { p: "په", f: "pu" },
after: { p: "کې", f: "ke" },
e: "in",
inside: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812828,"i":10539,"p":"کور","f":"kor","g":"kor","e":"house, home","c":"n. m."},
gender: "masc",
@ -124,6 +132,8 @@ We can also add a possesor it hangs outside of the sandwich. All together it's s
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527815251,"i":7802,"p":"سړی","f":"saRéy","g":"saRey","e":"man","c":"n. m.","ec":"man","ep":"men"},
gender: "masc",
@ -137,6 +147,8 @@ We can also add a possesor it hangs outside of the sandwich. All together it's s
},
},
},
},
},
]}</EditablePhraseDiagram>
Here's another example using the sandwich <InlinePs opts={opts} ps={{ p: "سره", f: "sara", e: "with" }} />:
@ -144,12 +156,14 @@ Here's another example using the sandwich <InlinePs opts={opts} ps={{ p: "سره
<EditablePhraseDiagram opts={opts}>{[
{
type: "AP",
block: {
selection: {
type: "sandwich",
before: { p: "له", f: "la" },
after: { p: "سره", f: "sara" },
e: "with",
inside: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527814159,"i":12723,"p":"ملګری","f":"malgúrey","g":"malgurey","e":"friend, companion","c":"n. m. anim. unisex"},
gender: "masc",
@ -160,6 +174,8 @@ Here's another example using the sandwich <InlinePs opts={opts} ps={{ p: "سره
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "pronoun",
distance: "far",
person: 0,
@ -168,6 +184,8 @@ Here's another example using the sandwich <InlinePs opts={opts} ps={{ p: "سره
},
},
},
},
},
]}</EditablePhraseDiagram>
Notice how when we put the word <InlinePs opts={opts} ps={{ p: "ملګری", f: "malgúrey", e: "friend" }} /> inside the sandwich it <Link to="/inflection/inflection-intro/">inflects</Link>. You <strong>always inflect the inside of the sandwich</strong> except for <Link to="/inflection/inflection-patterns/#exceptions">two exceptions</Link>.

View File

@ -10,9 +10,6 @@ import {
} from "@lingdocs/pashto-inflector";
import psmd from "../../lib/psmd";
import Link from "../../components/Link";
import EditablePhraseDiagram, {
EditIcon,
} from "../../components/phrase-diagram/EditablePhraseDiagram";
import EquativeIllustration from "../../components/EquativeIllustration";
import BasicBlocks from "../../components/BasicBlocks"

View File

@ -40,7 +40,7 @@ A **noun** is a word that we use to identify people, places, things, or ideas. O
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "noun",
entry: {"ts":1527812817,"i":9999,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},
gender: "masc",
@ -58,7 +58,7 @@ We can also **extend our noun by adding *adjectives***. Let's add the *adjective
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "noun",
entry: {"ts":1527812817,"i":9999,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},
gender: "masc",
@ -70,7 +70,7 @@ We can also **extend our noun by adding *adjectives***. Let's add the *adjective
entry: {"ts":1527815451,"i":7245,"p":"زوړ","f":"zoR","g":"zoR","e":"old","c":"adj. irreg.","infap":"زاړه","infaf":"zaaRu","infbp":"زړ","infbf":"zaR"},
}],
possesor: undefined,
},
}
},
]}</EditablePhraseDiagram>
@ -79,7 +79,7 @@ Now we have two words, but it's still **one NP**, one building block. We can add
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "noun",
entry: {"ts":1527812817,"i":9999,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},
gender: "masc",
@ -101,7 +101,7 @@ Now we have two words, but it's still **one NP**, one building block. We can add
},
],
possesor: undefined,
},
}
},
]}</EditablePhraseDiagram>
@ -110,7 +110,7 @@ We can also add a **possesor** by adding another NP <Link to="/sandwiches/sandwi
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "noun",
entry: {"ts":1527812817,"i":9999,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},
gender: "masc",
@ -126,6 +126,8 @@ We can also add a **possesor** by adding another NP <Link to="/sandwiches/sandwi
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812881,"i":11694,"p":"ماشوم","f":"maashoom","g":"maashoom","e":"child, kid","c":"n. m. anim. unisex","ec":"child","ep":"children"},
gender: "masc",
@ -133,6 +135,7 @@ We can also add a **possesor** by adding another NP <Link to="/sandwiches/sandwi
number: "singular",
numberCanChange: true,
adjectives: [],
}
},
},
},
@ -144,7 +147,7 @@ If our possesor is a noun, we can add a possesor to *it*. Try clicking the <Edit
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "noun",
entry: {"ts":1527812817,"i":9999,"p":"کتاب","f":"kitáab","g":"kitaab","e":"book","c":"n. m."},
gender: "masc",
@ -160,6 +163,8 @@ If our possesor is a noun, we can add a possesor to *it*. Try clicking the <Edit
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812881,"i":11694,"p":"ماشوم","f":"maashoom","g":"maashoom","e":"child, kid","c":"n. m. anim. unisex","ec":"child","ep":"children"},
gender: "masc",
@ -170,6 +175,8 @@ If our possesor is a noun, we can add a possesor to *it*. Try clicking the <Edit
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527815177,"i":2530,"p":"پلار","f":"plaar","g":"plaar","e":"father","c":"n. m."},
gender: "masc",
@ -183,6 +190,8 @@ If our possesor is a noun, we can add a possesor to *it*. Try clicking the <Edit
},
},
},
},
},
]}</EditablePhraseDiagram>
A possesor can have another possesor which can have another posseser and so-on and on *forever*. The nerdy word for this phenomenon where things reference/repeat themselves is [recursion](https://en.wikipedia.org/wiki/Recursion). 🤓
@ -206,23 +215,21 @@ You can't add any adjectives or possesors to pronouns in Pashto. They just stand
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "pronoun",
person: 0,
distance: "far",
},
}
},
]}</EditablePhraseDiagram>
<EditablePhraseDiagram opts={opts}>{[
{type: "NP", selection:
{
type: "NP",
block: {
type: "pronoun",
person: 11,
distance: "far",
},
},
}},
]}</EditablePhraseDiagram>
### Participle
@ -235,12 +242,12 @@ In Pashto you can use the infinitive form of a verb as a participle, meaning you
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "participle",
verb: {
entry: {"ts":1527812856,"i":11617,"p":"لیکل","f":"leekul","g":"leekul","e":"to write","c":"v. trans./gramm. trans.","ec":"write,writes,writing,wrote,written"},
},
},
}
},
]}</EditablePhraseDiagram>
@ -291,12 +298,12 @@ For example, if we take the participle <InlinePs opts={opts} ps={{ p: "وهل",
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "participle",
verb: {
entry: {"ts":1527815399,"i":14463,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"},
},
},
}
},
]}</EditablePhraseDiagram>
@ -305,7 +312,7 @@ And we can add the word <InlinePs opts={opts} ps={{ p: "ماشومان", f: "maa
<EditablePhraseDiagram opts={opts}>{[
{
type: "NP",
block: {
selection: {
type: "participle",
verb: {
entry: {"ts":1527815399,"i":14463,"p":"وهل","f":"wahul","g":"wahul","e":"to hit","c":"v. trans.","tppp":"واهه","tppf":"waahu","ec":"hit,hits,hitting,hit,hit"},
@ -313,6 +320,8 @@ And we can add the word <InlinePs opts={opts} ps={{ p: "ماشومان", f: "maa
possesor: {
shrunken: false,
np: {
type: "NP",
selection: {
type: "noun",
entry: {"ts":1527812881,"i":11694,"p":"ماشوم","f":"maashoom","g":"maashoom","e":"child, kid","c":"n. m. anim. unisex","ec":"child","ep":"children"},
gender: "masc",
@ -324,6 +333,7 @@ And we can add the word <InlinePs opts={opts} ps={{ p: "ماشومان", f: "maa
},
},
},
},
]}</EditablePhraseDiagram>
The noun we just attached can be a subject *or* an object of the participle. You just have to know from context. So this NP can mean either:

View File

@ -218,12 +218,15 @@ export default function EquativeGame({ id, link, level }: { id: string, link: st
};
}
function makeRandomEPS(l: T.EquativeTense | "allIdentify" | "allProduce"): T.EPSelectionComplete {
const subj = randFromArray([
const subj: T.NPSelection = {
type: "NP",
selection: randFromArray([
makeRandPronoun,
makeRandPronoun,
makeRandomNoun,
makeRandPronoun,
])();
])(),
};
const pred = randFromArray([...adjectives, ...locAdverbs]);
const tense = (l === "allIdentify" || l === "allProduce")
? randFromArray(tenses)
@ -258,7 +261,7 @@ export default function EquativeGame({ id, link, level }: { id: string, link: st
question: {
EPS,
phrase,
equative: EP.equative,
equative: getEqFromRendered(EP),
},
};
}
@ -478,7 +481,9 @@ function makeEPS(subject: T.NPSelection, predicate: T.AdjectiveEntry | T.Locativ
},
],
predicate: {
type: "Complement",
type: "predicateSelection",
selection: {
type: "EQComp",
selection: tp.isAdjectiveEntry(predicate) ? {
type: "adjective",
entry: predicate,
@ -488,6 +493,7 @@ function makeEPS(subject: T.NPSelection, predicate: T.AdjectiveEntry | T.Locativ
entry: predicate,
},
},
},
equative: {
tense,
negative: false,
@ -495,3 +501,9 @@ function makeEPS(subject: T.NPSelection, predicate: T.AdjectiveEntry | T.Locativ
omitSubject: false,
};
}
function getEqFromRendered(e: T.EPRendered): T.EquativeRendered {
const eblock = e.blocks.find(x => x.type === "equative");
if (!eblock || eblock.type !== "equative") throw new Error("Error getting equative block");
return eblock.equative;
}

View File

@ -4,10 +4,3 @@ type Pronoun = {
person: import("@lingdocs/pashto-inflector").Types.Person,
};
type BlockInput = {
type: "NP",
block: import("@lingdocs/pashto-inflector").Types.NPSelection,
} | {
type: "AP",
block: import("@lingdocs/pashto-inflector").Types.APSelection,
};

View File

@ -1695,10 +1695,10 @@
rambda "^6.7.0"
react-select "^5.2.2"
"@lingdocs/pashto-inflector@^2.6.8":
version "2.6.8"
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-2.6.8.tgz#d104620a75a4f034d3dcd9d8a097eab40b77024d"
integrity sha512-z2RetX9mRgZxM0FIX8R3cgCF+Exof/OigMDZIlEHLhBAljSwaojA9ZdXnyKP1zW8EDtjLIqsr3rWQre6niU80w==
"@lingdocs/pashto-inflector@^2.8.2":
version "2.8.2"
resolved "https://npm.lingdocs.com/@lingdocs%2fpashto-inflector/-/pashto-inflector-2.8.2.tgz#006f1f3f8223f5ea67ac75106369c3f6072441a3"
integrity sha512-mA9L37+Tq5teDTxczjfIBMfOStfqAzn7QFUNoRSEXl7ci4Yo+5bomHf2sjGDr0ZbneFAyp0K05tVJCWi8SBkeg==
dependencies:
"@formkit/auto-animate" "^1.0.0-beta.1"
classnames "^2.2.6"