added demonstrative pronouns
This commit is contained in:
parent
46fd6e66e8
commit
453216e6f1
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "pashto-inflector",
|
"name": "pashto-inflector",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pashto-inflector",
|
"name": "pashto-inflector",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "pashto-inflector",
|
"name": "pashto-inflector",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"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",
|
||||||
|
|
|
@ -20,7 +20,7 @@ import Hider from "./components/src/Hider";
|
||||||
import InflectionDemo from "./demo-components/InflectionDemo";
|
import InflectionDemo from "./demo-components/InflectionDemo";
|
||||||
import SpellingDemo from "./demo-components/SpellingDemo";
|
import SpellingDemo from "./demo-components/SpellingDemo";
|
||||||
import ParserDemo from "./demo-components/ParserDemo";
|
import ParserDemo from "./demo-components/ParserDemo";
|
||||||
import InflectionTable from "./components/src/InflectionsTable";
|
// import InflectionTable from "./components/src/InflectionsTable";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(
|
const [showingTextOptions, setShowingTextOptions] = useStickyState<boolean>(
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/ps-react",
|
"name": "@lingdocs/ps-react",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@lingdocs/ps-react",
|
"name": "@lingdocs/ps-react",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^1.0.0-beta.3",
|
"@formkit/auto-animate": "^1.0.0-beta.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/ps-react",
|
"name": "@lingdocs/ps-react",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"description": "Pashto inflector library module with React components",
|
"description": "Pashto inflector library module with React components",
|
||||||
"main": "dist/components/library.js",
|
"main": "dist/components/library.js",
|
||||||
"module": "dist/components/library.js",
|
"module": "dist/components/library.js",
|
||||||
|
|
|
@ -600,6 +600,27 @@ function Sandwich({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Demonstrative({
|
||||||
|
opts,
|
||||||
|
script,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
opts: T.TextOptions;
|
||||||
|
script: "p" | "f";
|
||||||
|
children: T.Rendered<T.DemonstrativeSelection> | undefined;
|
||||||
|
}) {
|
||||||
|
if (!children) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="text-center">
|
||||||
|
<Border padding={"1rem"}>{children.ps[script]}</Border>
|
||||||
|
<div>DEM</div>
|
||||||
|
<SubText>{children.e}</SubText>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function CompNounBlock({
|
function CompNounBlock({
|
||||||
opts,
|
opts,
|
||||||
noun,
|
noun,
|
||||||
|
@ -612,7 +633,7 @@ function CompNounBlock({
|
||||||
return (
|
return (
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Border
|
<Border
|
||||||
extraClassName={`!inside && hasPossesor ? "pt-2" : ""`}
|
// extraClassName={`${!inside && hasPossesor} ? "pt-2" : ""`}
|
||||||
padding={"1rem"}
|
padding={"1rem"}
|
||||||
>
|
>
|
||||||
{flattenLengths(noun.ps)[0][script]}
|
{flattenLengths(noun.ps)[0][script]}
|
||||||
|
@ -652,6 +673,9 @@ export function NPBlock({
|
||||||
</Possesors>,
|
</Possesors>,
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
<Demonstrative opts={opts} script={script}>
|
||||||
|
{np.selection.demonstrative ? np.selection.demonstrative : undefined}
|
||||||
|
</Demonstrative>,
|
||||||
<Adjectives opts={opts} script={script}>
|
<Adjectives opts={opts} script={script}>
|
||||||
{np.selection.adjectives}
|
{np.selection.adjectives}
|
||||||
</Adjectives>,
|
</Adjectives>,
|
||||||
|
@ -659,12 +683,15 @@ export function NPBlock({
|
||||||
{" "}
|
{" "}
|
||||||
{flattenLengths(np.selection.ps)[0][script]}
|
{flattenLengths(np.selection.ps)[0][script]}
|
||||||
</div>,
|
</div>,
|
||||||
];
|
].filter((x) => {
|
||||||
|
// @ts-ignore
|
||||||
|
return x !== " ";
|
||||||
|
});
|
||||||
const el = script === "p" ? elements.reverse() : elements;
|
const el = script === "p" ? elements.reverse() : elements;
|
||||||
return (
|
return (
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<Border
|
<Border
|
||||||
extraClassName={`!inside && hasPossesor ? "pt-2" : ""`}
|
// extraClassName={`!inside && hasPossesor ? "pt-2" : ""`}
|
||||||
padding={
|
padding={
|
||||||
inside
|
inside
|
||||||
? "0.3rem"
|
? "0.3rem"
|
||||||
|
@ -759,6 +786,9 @@ function Adjectives({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const c = script === "p" ? children.reverse() : children;
|
const c = script === "p" ? children.reverse() : children;
|
||||||
|
if (c.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<em className="mr-1">
|
<em className="mr-1">
|
||||||
{c.map((a) => a.ps[0][script]).join(" ")}
|
{c.map((a) => a.ps[0][script]).join(" ")}
|
||||||
|
|
|
@ -1,153 +1,98 @@
|
||||||
import * as T from "../../../types";
|
import * as T from "../../../types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import AdjectivePicker from "./AdjectivePicker";
|
import AdjectivePicker from "./AdjectivePicker";
|
||||||
import classNames from "classnames";
|
|
||||||
|
|
||||||
function AdjectiveManager(props: {
|
function AdjectiveManager(props: {
|
||||||
adjectives: T.AdjectiveSelection[],
|
adjectives: T.AdjectiveSelection[];
|
||||||
entryFeeder: T.EntryFeeder,
|
entryFeeder: T.EntryFeeder;
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions;
|
||||||
demonstrative: T.NounSelection["demonstrative"],
|
onChange: (adjs: T.AdjectiveSelection[]) => void;
|
||||||
onChange: (adjs: T.AdjectiveSelection[]) => void,
|
phraseIsComplete: boolean;
|
||||||
onDemonstrativeChange: (dem: T.NounSelection["demonstrative"]) => void,
|
|
||||||
phraseIsComplete: boolean,
|
|
||||||
}) {
|
}) {
|
||||||
const [adding, setAdding] = useState<boolean>(false);
|
const [adding, setAdding] = useState<boolean>(false);
|
||||||
const [addingDemonstrative, setAddingDemonstrative] = useState<boolean>(false);
|
function handleChange(i: number) {
|
||||||
function handleChange(i: number) {
|
return (a: T.AdjectiveSelection | undefined) => {
|
||||||
return (a: T.AdjectiveSelection | undefined) => {
|
if (a === undefined) return;
|
||||||
if (a === undefined) return;
|
const updated = [...props.adjectives];
|
||||||
const updated = [...props.adjectives];
|
updated[i] = a;
|
||||||
updated[i] = a;
|
props.onChange(updated.filter((x): x is T.AdjectiveSelection => !!x));
|
||||||
props.onChange(
|
};
|
||||||
updated.filter((x): x is T.AdjectiveSelection => !!x)
|
}
|
||||||
);
|
function deleteAdj(i: number) {
|
||||||
};
|
return () => {
|
||||||
}
|
props.onChange(remove(props.adjectives, i));
|
||||||
function deleteAdj(i: number) {
|
};
|
||||||
return () => {
|
}
|
||||||
props.onChange(remove(props.adjectives, i));
|
function handleAddNew(a: T.AdjectiveSelection | undefined) {
|
||||||
};
|
if (a === undefined) return;
|
||||||
}
|
setAdding(false);
|
||||||
function handleAddNew(a: T.AdjectiveSelection | undefined) {
|
props.onChange([a, ...props.adjectives]);
|
||||||
if (a === undefined) return;
|
}
|
||||||
setAdding(false);
|
// const flippedList = [...props.adjectives];
|
||||||
props.onChange([
|
// flippedList.reverse();
|
||||||
a,
|
// console.log(props.adjectives);
|
||||||
...props.adjectives,
|
return (
|
||||||
]);
|
<div className="mb-1">
|
||||||
}
|
{adding && (
|
||||||
// const flippedList = [...props.adjectives];
|
<div>
|
||||||
// flippedList.reverse();
|
<div className="d-flex flex-row justify-content-between mb-1">
|
||||||
// console.log(props.adjectives);
|
<div>Add Adjective</div>
|
||||||
return <div className="mb-1">
|
<div className="clickable" onClick={() => setAdding(false)}>
|
||||||
{adding && <div>
|
<i className="fas fa-trash" />
|
||||||
<div className="d-flex flex-row justify-content-between mb-1">
|
|
||||||
<div>Add Adjective</div>
|
|
||||||
<div className="clickable" onClick={() => setAdding(false)}>
|
|
||||||
<i className="fas fa-trash" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<AdjectivePicker
|
</div>
|
||||||
phraseIsComplete={props.phraseIsComplete}
|
<AdjectivePicker
|
||||||
noTitle
|
phraseIsComplete={props.phraseIsComplete}
|
||||||
adjective={undefined}
|
noTitle
|
||||||
entryFeeder={props.entryFeeder}
|
adjective={undefined}
|
||||||
opts={props.opts}
|
entryFeeder={props.entryFeeder}
|
||||||
onChange={handleAddNew}
|
opts={props.opts}
|
||||||
/>
|
onChange={handleAddNew}
|
||||||
</div>}
|
/>
|
||||||
{addingDemonstrative && <div>
|
</div>
|
||||||
<div className="d-flex flex-row justify-content-between mb-1">
|
)}
|
||||||
<div>Add Demonstrative</div>
|
|
||||||
<div className="clickable" onClick={() => {
|
|
||||||
setAddingDemonstrative(false);
|
|
||||||
props.onDemonstrativeChange(undefined);
|
|
||||||
}}>
|
|
||||||
<i className="fas fa-trash" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DemonstrativePicker
|
|
||||||
demonstrative={props.demonstrative}
|
|
||||||
onChange={props.onDemonstrativeChange}
|
|
||||||
/>
|
|
||||||
</div>}
|
|
||||||
{props.adjectives.map((adj, i) => <div key={i}>
|
|
||||||
<div className="d-flex flex-row justify-content-between">
|
|
||||||
<div>Adjective</div>
|
|
||||||
<div className="d-flex flex-row align-items-baseline">
|
|
||||||
{!!props.adjectives.length && !adding && <div>
|
|
||||||
<div className="clickable h6" onClick={() => setAdding(true)}>+ Adj.</div>
|
|
||||||
</div>}
|
|
||||||
<div onClick={deleteAdj(i)} className="ml-4">
|
|
||||||
<div className="fas fa-trash" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AdjectivePicker
|
|
||||||
phraseIsComplete={props.phraseIsComplete}
|
|
||||||
noTitle
|
|
||||||
key={`adj${i}`}
|
|
||||||
adjective={adj}
|
|
||||||
entryFeeder={props.entryFeeder}
|
|
||||||
opts={props.opts}
|
|
||||||
onChange={handleChange(i)}
|
|
||||||
/>
|
|
||||||
</div>)}
|
|
||||||
{!adding && !props.adjectives.length && <div className="h6 clickable" style={{ float: "right" }}>
|
|
||||||
<div className="clickable" onClick={() => setAdding(true)}>+ Adj.</div>
|
|
||||||
</div>}
|
|
||||||
{/* {!addingDemonstrative && !props.demonstrative && <h6 className="clickable mr-2" style={{ float: "right" }}>
|
|
||||||
<div className="clickable" onClick={() => setAddingDemonstrative(true)}>+ Demons.</div>
|
|
||||||
</h6>} */}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function DemonstrativePicker({ demonstrative, onChange }: {
|
{props.adjectives.map((adj, i) => (
|
||||||
demonstrative: T.NounSelection["demonstrative"],
|
<div key={i}>
|
||||||
onChange: (dem: T.NounSelection["demonstrative"]) => void,
|
<div className="d-flex flex-row justify-content-between">
|
||||||
}) {
|
<div>Adjective</div>
|
||||||
function handleDChange(d: "daa" | "hagha" | "dagha") {
|
<div className="d-flex flex-row align-items-baseline">
|
||||||
if (!demonstrative) {
|
{!!props.adjectives.length && !adding && (
|
||||||
onChange({
|
<div>
|
||||||
type: "demonstrative",
|
<div className="clickable h6" onClick={() => setAdding(true)}>
|
||||||
demonstrative: d,
|
+ Adj.
|
||||||
hideNoun: false,
|
</div>
|
||||||
});
|
</div>
|
||||||
} else {
|
)}
|
||||||
onChange({
|
<div onClick={deleteAdj(i)} className="ml-4">
|
||||||
...demonstrative,
|
<div className="fas fa-trash" />
|
||||||
demonstrative: d,
|
</div>
|
||||||
});
|
</div>
|
||||||
}
|
</div>
|
||||||
}
|
<AdjectivePicker
|
||||||
return <div className="d-flex flex-row justify-content-around py-1">
|
phraseIsComplete={props.phraseIsComplete}
|
||||||
<div>
|
noTitle
|
||||||
<button
|
key={`adj${i}`}
|
||||||
className={classNames("btn", "btn-outline-secondary", { active: demonstrative?.demonstrative === "daa" })}
|
adjective={adj}
|
||||||
onClick={() => handleDChange("daa")}
|
entryFeeder={props.entryFeeder}
|
||||||
>دا</button>
|
opts={props.opts}
|
||||||
|
onChange={handleChange(i)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
))}
|
||||||
<button
|
{!adding && !props.adjectives.length && (
|
||||||
className={classNames("btn", "btn-outline-secondary", { active: demonstrative?.demonstrative === "hagha" })}
|
<div className="h6 clickable" style={{ float: "right" }}>
|
||||||
onClick={() => handleDChange("hagha")}
|
<div className="clickable" onClick={() => setAdding(true)}>
|
||||||
>هغه</button>
|
+ Adj.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
)}
|
||||||
<button
|
</div>
|
||||||
className={classNames("btn", "btn-outline-secondary", { active: demonstrative?.demonstrative === "dagha" })}
|
);
|
||||||
onClick={() => handleDChange("dagha")}
|
|
||||||
>دغه</button>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove<X>(arr: X[], i: number): X[] {
|
function remove<X>(arr: X[], i: number): X[] {
|
||||||
return [
|
return [...arr.slice(0, i), ...arr.slice(i + 1)];
|
||||||
...arr.slice(0, i),
|
|
||||||
...arr.slice(i + 1),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AdjectiveManager;
|
export default AdjectiveManager;
|
|
@ -0,0 +1,59 @@
|
||||||
|
import * as T from "../../../types";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
export default function DemonstrativePicker({
|
||||||
|
demonstrative,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
demonstrative: T.NounSelection["demonstrative"];
|
||||||
|
onChange: (dem: T.NounSelection["demonstrative"]) => void;
|
||||||
|
}) {
|
||||||
|
function handleDChange(d: "daa" | "hagha" | "dagha") {
|
||||||
|
if (!demonstrative) {
|
||||||
|
onChange({
|
||||||
|
type: "demonstrative",
|
||||||
|
demonstrative: d,
|
||||||
|
hideNoun: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onChange({
|
||||||
|
...demonstrative,
|
||||||
|
demonstrative: d,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-row justify-content-around py-1">
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
className={classNames("btn", "btn-outline-secondary", {
|
||||||
|
active: demonstrative?.demonstrative === "daa",
|
||||||
|
})}
|
||||||
|
onClick={() => handleDChange("daa")}
|
||||||
|
>
|
||||||
|
دا
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
className={classNames("btn", "btn-outline-secondary", {
|
||||||
|
active: demonstrative?.demonstrative === "dagha",
|
||||||
|
})}
|
||||||
|
onClick={() => handleDChange("dagha")}
|
||||||
|
>
|
||||||
|
دغه
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
className={classNames("btn", "btn-outline-secondary", {
|
||||||
|
active: demonstrative?.demonstrative === "hagha",
|
||||||
|
})}
|
||||||
|
onClick={() => handleDChange("hagha")}
|
||||||
|
>
|
||||||
|
هغه
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ import InlinePs from "../InlinePs";
|
||||||
// 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";
|
import EntrySelect from "../EntrySelect";
|
||||||
import AdjectiveManager from "./AdjectiveManager";
|
import AdjectiveManager from "./AdjectiveManager";
|
||||||
|
import { useState } from "react";
|
||||||
|
import DemonstrativePicker from "./DemonstrativePicker";
|
||||||
|
|
||||||
// const filterOptions = [
|
// const filterOptions = [
|
||||||
// {
|
// {
|
||||||
|
@ -61,6 +63,8 @@ function NPNounPicker(props: {
|
||||||
opts: T.TextOptions;
|
opts: T.TextOptions;
|
||||||
phraseIsComplete: boolean;
|
phraseIsComplete: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const [addingDemonstrative, setAddingDemonstrative] =
|
||||||
|
useState<boolean>(false);
|
||||||
// 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 nounsFiltered = props.nouns
|
// const nounsFiltered = props.nouns
|
||||||
|
@ -84,7 +88,7 @@ function NPNounPicker(props: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function handleDemonstrativeUpdate(
|
function handleDemonstrativeChange(
|
||||||
demonstrative: undefined | T.NounSelection["demonstrative"]
|
demonstrative: undefined | T.NounSelection["demonstrative"]
|
||||||
) {
|
) {
|
||||||
if (props.noun) {
|
if (props.noun) {
|
||||||
|
@ -96,6 +100,36 @@ function NPNounPicker(props: {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div style={{ maxWidth: "225px", minWidth: "125px" }}>
|
<div style={{ maxWidth: "225px", minWidth: "125px" }}>
|
||||||
|
{!addingDemonstrative && !props.noun?.demonstrative ? (
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
className="clickable text-muted"
|
||||||
|
onClick={() => setAddingDemonstrative(true)}
|
||||||
|
>
|
||||||
|
+ Demonstrative
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<div className="d-flex flex-row justify-content-between mb-1">
|
||||||
|
<div>{!props.noun?.demonstrative ? "Add" : ""} Demonstrative</div>
|
||||||
|
<div
|
||||||
|
className="clickable"
|
||||||
|
onClick={() => {
|
||||||
|
handleDemonstrativeChange(undefined);
|
||||||
|
setAddingDemonstrative(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="fas fa-trash" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<DemonstrativePicker
|
||||||
|
demonstrative={props.noun?.demonstrative}
|
||||||
|
onChange={handleDemonstrativeChange}
|
||||||
|
/>
|
||||||
|
</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="d-flex flex-row justify-content-between">
|
||||||
<div className="text-small mb-1">Filter by inflection pattern</div>
|
<div className="text-small mb-1">Filter by inflection pattern</div>
|
||||||
|
@ -113,11 +147,9 @@ function NPNounPicker(props: {
|
||||||
<AdjectiveManager
|
<AdjectiveManager
|
||||||
phraseIsComplete={props.phraseIsComplete}
|
phraseIsComplete={props.phraseIsComplete}
|
||||||
adjectives={props.noun?.adjectives}
|
adjectives={props.noun?.adjectives}
|
||||||
demonstrative={props.noun.demonstrative}
|
|
||||||
entryFeeder={props.entryFeeder}
|
entryFeeder={props.entryFeeder}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
onChange={handelAdjectivesUpdate}
|
onChange={handelAdjectivesUpdate}
|
||||||
onDemonstrativeChange={handleDemonstrativeUpdate}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="h6">Noun</div>
|
<div className="h6">Noun</div>
|
||||||
|
|
|
@ -1,272 +1,371 @@
|
||||||
import PronounPicker from "./NPPronounPicker";
|
import PronounPicker from "./NPPronounPicker";
|
||||||
import NounPicker from "./NPNounPicker";
|
import NounPicker from "./NPNounPicker";
|
||||||
import ParticiplePicker from "./NPParticiplePicker";
|
import ParticiplePicker from "./NPParticiplePicker";
|
||||||
import {
|
import { randomPerson } from "../../../lib/src/np-tools";
|
||||||
randomPerson,
|
|
||||||
} from "../../../lib/src/np-tools";
|
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import * as T from "../../../types";
|
import * as T from "../../../types";
|
||||||
import { isSecondPerson } from "../../../lib/src/misc-helpers";
|
import { isSecondPerson } from "../../../lib/src/misc-helpers";
|
||||||
|
|
||||||
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
|
const npTypes: T.NPType[] = ["pronoun", "noun", "participle"];
|
||||||
|
|
||||||
|
// TODO: BUG WITH PICKING IMPERATIVE FORMS OUTSIDE OF SECOND PERSON!
|
||||||
|
|
||||||
export const shrunkenBackground = "rgba(255, 206, 43, 0.15)";
|
export const shrunkenBackground = "rgba(255, 206, 43, 0.15)";
|
||||||
|
|
||||||
function NPPicker(props: {
|
function NPPicker(props: {
|
||||||
heading?: JSX.Element | string,
|
heading?: JSX.Element | string;
|
||||||
onChange: (nps: T.NPSelection | undefined) => void,
|
onChange: (nps: T.NPSelection | undefined) => void;
|
||||||
np: T.NPSelection | undefined,
|
np: T.NPSelection | undefined;
|
||||||
counterPart: T.NPSelection | T.VerbObject | undefined,
|
counterPart: T.NPSelection | T.VerbObject | undefined;
|
||||||
role: "subject" | "object" | "ergative" | "possesor",
|
role: "subject" | "object" | "ergative" | "possesor";
|
||||||
opts: T.TextOptions,
|
opts: T.TextOptions;
|
||||||
cantClear?: boolean,
|
cantClear?: boolean;
|
||||||
is2ndPersonPicker?: boolean,
|
is2ndPersonPicker?: boolean;
|
||||||
entryFeeder: T.EntryFeeder,
|
entryFeeder: T.EntryFeeder;
|
||||||
phraseIsComplete: boolean,
|
phraseIsComplete: boolean;
|
||||||
isShrunk?: boolean,
|
isShrunk?: boolean;
|
||||||
isRemoved?: boolean,
|
isRemoved?: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (props.is2ndPersonPicker && ((props.np?.selection.type !== "pronoun") || !isSecondPerson(props.np.selection.person))) {
|
if (
|
||||||
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
|
props.is2ndPersonPicker &&
|
||||||
|
(props.np?.selection.type !== "pronoun" ||
|
||||||
|
!isSecondPerson(props.np.selection.person))
|
||||||
|
) {
|
||||||
|
throw new Error("can't use 2ndPerson NPPicker without a pronoun");
|
||||||
|
}
|
||||||
|
const [addingPoss, setAddingPoss] = useState<boolean>(false);
|
||||||
|
const [npType, setNpType] = useState<T.NPType | undefined>(
|
||||||
|
props.np ? props.np.selection.type : undefined
|
||||||
|
);
|
||||||
|
const onChange = (np: T.NPSelection | undefined) => {
|
||||||
|
props.onChange(ensureSingleShrink(props.np, np));
|
||||||
|
if (
|
||||||
|
(np?.selection.type === "noun" || np?.selection.type === "participle") &&
|
||||||
|
np.selection.possesor
|
||||||
|
) {
|
||||||
|
setAddingPoss(true);
|
||||||
}
|
}
|
||||||
const [addingPoss, setAddingPoss] = useState<boolean>(false);
|
setAddingPoss(false);
|
||||||
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.selection.type : undefined);
|
};
|
||||||
const onChange = (np: T.NPSelection | undefined) => {
|
useEffect(() => {
|
||||||
props.onChange(ensureSingleShrink(props.np, np));
|
setNpType(props.np ? props.np.selection.type : undefined);
|
||||||
if ((np?.selection.type === "noun" || np?.selection.type === "participle") && np.selection.possesor) {
|
}, [props.np]);
|
||||||
setAddingPoss(true);
|
function handleClear() {
|
||||||
}
|
if (
|
||||||
setAddingPoss(false);
|
props.np &&
|
||||||
}
|
props.np.selection.type === "noun" &&
|
||||||
useEffect(() => {
|
props.np.selection.dynamicComplement
|
||||||
setNpType(props.np ? props.np.selection.type : undefined);
|
)
|
||||||
}, [props.np]);
|
return;
|
||||||
function handleClear() {
|
setNpType(undefined);
|
||||||
if (props.np && props.np.selection.type === "noun" && props.np.selection.dynamicComplement) return;
|
onChange(undefined);
|
||||||
setNpType(undefined);
|
}
|
||||||
|
function handleNPTypeChange(ntp: T.NPType) {
|
||||||
|
if (ntp === "pronoun") {
|
||||||
|
const person = randomPerson({ counterPart: props.counterPart });
|
||||||
|
const pronoun: T.PronounSelection = {
|
||||||
|
type: "pronoun",
|
||||||
|
person,
|
||||||
|
distance: "far",
|
||||||
|
};
|
||||||
|
setNpType(ntp);
|
||||||
|
onChange({ type: "NP", selection: pronoun });
|
||||||
|
} else {
|
||||||
|
if (props.np) {
|
||||||
onChange(undefined);
|
onChange(undefined);
|
||||||
|
}
|
||||||
|
setNpType(ntp);
|
||||||
}
|
}
|
||||||
function handleNPTypeChange(ntp: T.NPType) {
|
}
|
||||||
if (ntp === "pronoun") {
|
// TODO: REMOVE
|
||||||
const person = randomPerson({ counterPart: props.counterPart });
|
function handlePossesiveChange(p: T.NPSelection | undefined) {
|
||||||
const pronoun: T.PronounSelection = {
|
if (!props.np || props.np.selection.type === "pronoun") return;
|
||||||
type: "pronoun",
|
if (!p) {
|
||||||
person,
|
onChange({
|
||||||
distance: "far",
|
type: "NP",
|
||||||
};
|
selection: {
|
||||||
setNpType(ntp);
|
...props.np.selection,
|
||||||
onChange({ type: "NP", selection: pronoun });
|
possesor: undefined,
|
||||||
} else {
|
},
|
||||||
if (props.np) {
|
});
|
||||||
onChange(undefined);
|
return;
|
||||||
}
|
|
||||||
setNpType(ntp);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// TODO: REMOVE
|
const isNewPosesser = checkForNewPossesor(p, props.np.selection.possesor);
|
||||||
function handlePossesiveChange(p: T.NPSelection | undefined) {
|
const possesor: T.PossesorSelection = {
|
||||||
if (!props.np || props.np.selection.type === "pronoun") return;
|
np: p,
|
||||||
if (!p) {
|
shrunken:
|
||||||
onChange({
|
!isNewPosesser && props.np.selection.possesor
|
||||||
type: "NP",
|
? props.np.selection.possesor.shrunken
|
||||||
selection: {
|
: false,
|
||||||
...props.np.selection,
|
};
|
||||||
possesor: undefined,
|
onChange({
|
||||||
},
|
type: "NP",
|
||||||
});
|
selection: {
|
||||||
return;
|
...props.np.selection,
|
||||||
}
|
possesor,
|
||||||
const isNewPosesser = checkForNewPossesor(p, props.np.selection.possesor);
|
},
|
||||||
const possesor: T.PossesorSelection = {
|
});
|
||||||
np: p,
|
}
|
||||||
shrunken: (!isNewPosesser && props.np.selection.possesor) ? props.np.selection.possesor.shrunken : false,
|
function handleToggleShrunken() {
|
||||||
};
|
if (
|
||||||
onChange({
|
!props.np ||
|
||||||
type: "NP",
|
props.np.selection.type === "pronoun" ||
|
||||||
selection: {
|
!props.np.selection.possesor ||
|
||||||
...props.np.selection,
|
!props.phraseIsComplete
|
||||||
possesor,
|
)
|
||||||
},
|
return;
|
||||||
});
|
onChange({
|
||||||
}
|
type: "NP",
|
||||||
function handleToggleShrunken() {
|
selection: {
|
||||||
if (!props.np || props.np.selection.type === "pronoun" || !props.np.selection.possesor || !props.phraseIsComplete) return;
|
...props.np.selection,
|
||||||
onChange({
|
possesor: {
|
||||||
type: "NP",
|
...props.np.selection.possesor,
|
||||||
selection: {
|
shrunken: !props.np.selection.possesor.shrunken,
|
||||||
...props.np.selection,
|
},
|
||||||
possesor: {
|
},
|
||||||
...props.np.selection.possesor,
|
});
|
||||||
shrunken: !props.np.selection.possesor.shrunken,
|
}
|
||||||
},
|
const isDynamicComplement =
|
||||||
},
|
props.np &&
|
||||||
});
|
props.np.selection.type === "noun" &&
|
||||||
}
|
props.np.selection.dynamicComplement;
|
||||||
const isDynamicComplement = props.np && props.np.selection.type === "noun" && props.np.selection.dynamicComplement;
|
const clearButton =
|
||||||
const clearButton = (!props.cantClear && !props.is2ndPersonPicker && !isDynamicComplement)
|
!props.cantClear && !props.is2ndPersonPicker && !isDynamicComplement ? (
|
||||||
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
|
<button className="btn btn-sm btn-light mb-2" onClick={handleClear}>
|
||||||
: <div></div>;
|
X
|
||||||
const possesiveLabel = props.np?.selection.type === "participle" ? "Subj/Obj" : "Possesor";
|
</button>
|
||||||
return <div style={{
|
) : (
|
||||||
|
<div></div>
|
||||||
|
);
|
||||||
|
const possesiveLabel =
|
||||||
|
props.np?.selection.type === "participle" ? "Subj/Obj" : "Possesor";
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
opacity: props.isRemoved ? 0.5 : 1,
|
opacity: props.isRemoved ? 0.5 : 1,
|
||||||
}}>
|
}}
|
||||||
<div className="d-flex flex-row justify-content-between">
|
>
|
||||||
<div></div>
|
<div className="d-flex flex-row justify-content-between">
|
||||||
<div>
|
<div></div>
|
||||||
{typeof props.heading === "string"
|
<div>
|
||||||
? <div className="h5 text-center">{props.heading}</div>
|
{typeof props.heading === "string" ? (
|
||||||
: props.heading}
|
<div className="h5 text-center">{props.heading}</div>
|
||||||
</div>
|
) : (
|
||||||
<div>
|
props.heading
|
||||||
{npType && clearButton}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ minWidth: "9rem" }}>
|
<div>{npType && clearButton}</div>
|
||||||
{!npType && <div className="text-center">
|
</div>
|
||||||
<div className="h6 mr-3">
|
<div style={{ minWidth: "9rem" }}>
|
||||||
Choose NP
|
{!npType && (
|
||||||
</div>
|
<div className="text-center">
|
||||||
{npTypes.map((npt) => <div key={npt} className="mb-2">
|
<div className="h6 mr-3">Choose NP</div>
|
||||||
<button
|
{npTypes.map((npt) => (
|
||||||
key={npt}
|
<div key={npt} className="mb-2">
|
||||||
type="button"
|
<button
|
||||||
className="mr-2 btn btn-sm btn-outline-secondary"
|
key={npt}
|
||||||
onClick={() => handleNPTypeChange(npt)}
|
type="button"
|
||||||
>
|
className="mr-2 btn btn-sm btn-outline-secondary"
|
||||||
{npt}
|
onClick={() => handleNPTypeChange(npt)}
|
||||||
</button>
|
>
|
||||||
</div>)}
|
{npt}
|
||||||
</div>}
|
</button>
|
||||||
{(props.np && props.np.selection.type !== "pronoun" && (props.np.selection.possesor || addingPoss)) && <div className="mb-3" style={{
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.np &&
|
||||||
|
props.np.selection.type !== "pronoun" &&
|
||||||
|
(props.np.selection.possesor || addingPoss) && (
|
||||||
|
<div
|
||||||
|
className="mb-3"
|
||||||
|
style={{
|
||||||
paddingLeft: "0.65rem",
|
paddingLeft: "0.65rem",
|
||||||
borderLeft: "2px solid grey",
|
borderLeft: "2px solid grey",
|
||||||
background: (props.np.selection.possesor?.shrunken && !props.isShrunk) ? shrunkenBackground : "inherit",
|
background:
|
||||||
}}>
|
props.np.selection.possesor?.shrunken && !props.isShrunk
|
||||||
<div className="d-flex flex-row text-muted mb-2">
|
? shrunkenBackground
|
||||||
<div>{possesiveLabel}:</div>
|
: "inherit",
|
||||||
{(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="d-flex flex-row text-muted mb-2">
|
||||||
<div className="clickable ml-2" onClick={() => {
|
<div>{possesiveLabel}:</div>
|
||||||
setAddingPoss(false);
|
{props.np.selection.possesor &&
|
||||||
handlePossesiveChange(undefined);
|
!props.isShrunk &&
|
||||||
}}>
|
props.phraseIsComplete && (
|
||||||
<i className="fas fa-trash" />
|
<div
|
||||||
|
className="clickable ml-3 mr-2"
|
||||||
|
onClick={handleToggleShrunken}
|
||||||
|
>
|
||||||
|
{!props.np.selection.possesor.shrunken ? "🪄" : "👶"}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className="clickable ml-2"
|
||||||
|
onClick={() => {
|
||||||
|
setAddingPoss(false);
|
||||||
|
handlePossesiveChange(undefined);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="fas fa-trash" />
|
||||||
</div>
|
</div>
|
||||||
<NPPicker
|
</div>
|
||||||
phraseIsComplete={props.phraseIsComplete}
|
<NPPicker
|
||||||
onChange={handlePossesiveChange}
|
phraseIsComplete={props.phraseIsComplete}
|
||||||
counterPart={undefined}
|
onChange={handlePossesiveChange}
|
||||||
cantClear
|
counterPart={undefined}
|
||||||
np={props.np.selection.possesor ? props.np.selection.possesor.np : undefined}
|
cantClear
|
||||||
role="possesor"
|
np={
|
||||||
opts={props.opts}
|
props.np.selection.possesor
|
||||||
entryFeeder={props.entryFeeder}
|
? props.np.selection.possesor.np
|
||||||
/>
|
: undefined
|
||||||
</div>}
|
}
|
||||||
{(npType === "noun" || npType === "participle") && props.np && !addingPoss && <div>
|
role="possesor"
|
||||||
<span className="clickable text-muted" onClick={() => setAddingPoss(true)}>+ {possesiveLabel}</span>
|
opts={props.opts}
|
||||||
</div>}
|
entryFeeder={props.entryFeeder}
|
||||||
{(npType === "pronoun" && props.np?.selection.type === "pronoun")
|
/>
|
||||||
? <PronounPicker
|
</div>
|
||||||
role={props.role}
|
)}
|
||||||
pronoun={props.np.selection}
|
{(npType === "noun" || npType === "participle") &&
|
||||||
onChange={(p) => onChange({ type: "NP", selection: p })}
|
props.np &&
|
||||||
is2ndPersonPicker={props.is2ndPersonPicker}
|
!addingPoss && (
|
||||||
opts={props.opts}
|
<div>
|
||||||
/>
|
<span
|
||||||
: npType === "noun"
|
className="clickable text-muted"
|
||||||
? <NounPicker
|
onClick={() => setAddingPoss(true)}
|
||||||
phraseIsComplete={props.phraseIsComplete}
|
>
|
||||||
entryFeeder={props.entryFeeder}
|
+ {possesiveLabel}
|
||||||
noun={(props.np && props.np.selection.type === "noun") ? props.np.selection : undefined}
|
</span>
|
||||||
onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
|
</div>
|
||||||
opts={props.opts}
|
)}
|
||||||
/>
|
{npType === "pronoun" && props.np?.selection.type === "pronoun" ? (
|
||||||
: npType === "participle"
|
<PronounPicker
|
||||||
? <ParticiplePicker
|
role={props.role}
|
||||||
entryFeeder={props.entryFeeder.verbs}
|
pronoun={props.np.selection}
|
||||||
participle={(props.np && props.np.selection.type === "participle") ? props.np.selection : undefined}
|
onChange={(p) => onChange({ type: "NP", selection: p })}
|
||||||
onChange={(s) => onChange(s ? { type: "NP", selection: s } : undefined)}
|
is2ndPersonPicker={props.is2ndPersonPicker}
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
/>
|
/>
|
||||||
: null
|
) : npType === "noun" ? (
|
||||||
|
<NounPicker
|
||||||
|
phraseIsComplete={props.phraseIsComplete}
|
||||||
|
entryFeeder={props.entryFeeder}
|
||||||
|
noun={
|
||||||
|
props.np && props.np.selection.type === "noun"
|
||||||
|
? props.np.selection
|
||||||
|
: undefined
|
||||||
}
|
}
|
||||||
</div>
|
onChange={(s) =>
|
||||||
</div>;
|
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>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureSingleShrink(old: T.NPSelection | undefined, s: T.NPSelection | undefined): T.NPSelection | undefined {
|
function ensureSingleShrink(
|
||||||
if (!s) return s;
|
old: T.NPSelection | undefined,
|
||||||
function countShrinks(np: T.NPSelection): number {
|
s: T.NPSelection | undefined
|
||||||
if (np.selection.type === "pronoun") return 0;
|
): T.NPSelection | undefined {
|
||||||
if (!np.selection.possesor) return 0;
|
if (!s) return s;
|
||||||
return (np.selection.possesor.shrunken ? 1 : 0) + countShrinks(np.selection.possesor.np);
|
function countShrinks(np: T.NPSelection): number {
|
||||||
|
if (np.selection.type === "pronoun") return 0;
|
||||||
|
if (!np.selection.possesor) return 0;
|
||||||
|
return (
|
||||||
|
(np.selection.possesor.shrunken ? 1 : 0) +
|
||||||
|
countShrinks(np.selection.possesor.np)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function keepNewShrink(old: T.NPSelection, n: T.NPSelection): T.NPSelection {
|
||||||
|
if (n.selection.type === "pronoun") return n;
|
||||||
|
if (
|
||||||
|
old.selection.type === "pronoun" ||
|
||||||
|
!n.selection.possesor ||
|
||||||
|
!old.selection.possesor
|
||||||
|
)
|
||||||
|
return n;
|
||||||
|
if (n.selection.possesor.shrunken && !old.selection.possesor.shrunken) {
|
||||||
|
return {
|
||||||
|
type: "NP",
|
||||||
|
selection: {
|
||||||
|
...n.selection,
|
||||||
|
possesor: {
|
||||||
|
...n.selection.possesor,
|
||||||
|
np: removeShrinks(n.selection.possesor.np),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
function keepNewShrink(old: T.NPSelection, n: T.NPSelection): T.NPSelection {
|
return {
|
||||||
if (n.selection.type === "pronoun") return n;
|
type: "NP",
|
||||||
if (old.selection.type === "pronoun" || !n.selection.possesor || !old.selection.possesor) return n;
|
selection: {
|
||||||
if (n.selection.possesor.shrunken && !old.selection.possesor.shrunken) {
|
...n.selection,
|
||||||
return {
|
possesor: {
|
||||||
type: "NP",
|
shrunken: false,
|
||||||
selection: {
|
np: keepNewShrink(old.selection.possesor.np, n.selection.possesor.np),
|
||||||
...n.selection,
|
},
|
||||||
possesor: {
|
},
|
||||||
...n.selection.possesor,
|
};
|
||||||
np: removeShrinks(n.selection.possesor.np),
|
}
|
||||||
},
|
function removeShrinks(n: T.NPSelection): T.NPSelection {
|
||||||
},
|
if (n.selection.type === "pronoun") return n;
|
||||||
};
|
if (!n.selection.possesor) return n;
|
||||||
}
|
return {
|
||||||
return {
|
type: "NP",
|
||||||
type: "NP",
|
selection: {
|
||||||
selection:{
|
...n.selection,
|
||||||
...n.selection,
|
possesor: {
|
||||||
possesor: {
|
shrunken: false,
|
||||||
shrunken: false,
|
np: removeShrinks(n.selection.possesor.np),
|
||||||
np: keepNewShrink(old.selection.possesor.np, n.selection.possesor.np),
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
}
|
if (!old) return s;
|
||||||
function removeShrinks(n: T.NPSelection): T.NPSelection {
|
if (s.selection.type === "pronoun") return s;
|
||||||
if (n.selection.type === "pronoun") return n;
|
if (!s.selection.possesor) return s;
|
||||||
if (!n.selection.possesor) return n;
|
const numOfShrinks = countShrinks(s);
|
||||||
return {
|
if (numOfShrinks < 2) return s;
|
||||||
type: "NP",
|
return keepNewShrink(old, s);
|
||||||
selection: {
|
|
||||||
...n.selection,
|
|
||||||
possesor: {
|
|
||||||
shrunken: false,
|
|
||||||
np: removeShrinks(n.selection.possesor.np),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!old) return s;
|
|
||||||
if (s.selection.type === "pronoun") return s;
|
|
||||||
if (!s.selection.possesor) return s;
|
|
||||||
const numOfShrinks = countShrinks(s);
|
|
||||||
if (numOfShrinks < 2) return s;
|
|
||||||
return keepNewShrink(old, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForNewPossesor(n: T.NPSelection | undefined, old: T.PossesorSelection | undefined): boolean {
|
function checkForNewPossesor(
|
||||||
if (!old || !n) {
|
n: T.NPSelection | undefined,
|
||||||
return true;
|
old: T.PossesorSelection | undefined
|
||||||
}
|
): boolean {
|
||||||
if (n.type !== old.np.type) {
|
if (!old || !n) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (n.selection.type === "pronoun") return false;
|
if (n.type !== old.np.type) {
|
||||||
if (n.selection.type === "noun" && old.np.selection.type === "noun") {
|
return true;
|
||||||
return n.selection.entry.ts !== old.np.selection.entry.ts;
|
}
|
||||||
}
|
if (n.selection.type === "pronoun") return false;
|
||||||
if (n.selection.type === "participle" && old.np.selection.type === "participle") {
|
if (n.selection.type === "noun" && old.np.selection.type === "noun") {
|
||||||
return n.selection.verb.entry.ts !== old.np.selection.verb.entry.ts;
|
return n.selection.entry.ts !== old.np.selection.entry.ts;
|
||||||
}
|
}
|
||||||
return false;
|
if (
|
||||||
|
n.selection.type === "participle" &&
|
||||||
|
old.np.selection.type === "participle"
|
||||||
|
) {
|
||||||
|
return n.selection.verb.entry.ts !== old.np.selection.verb.entry.ts;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NPPicker;
|
export default NPPicker;
|
|
@ -81,7 +81,6 @@ function InflectionDemo({ opts }: { opts: T.TextOptions }) {
|
||||||
const v = e.target.value;
|
const v = e.target.value;
|
||||||
const value = v === "all" ? v : (Number(v) as T.InflectionPattern);
|
const value = v === "all" ? v : (Number(v) as T.InflectionPattern);
|
||||||
setPattern(value);
|
setPattern(value);
|
||||||
console.log({ word });
|
|
||||||
if (word && !tp.isPattern(value)(word)) {
|
if (word && !tp.isPattern(value)(word)) {
|
||||||
setWord(undefined);
|
setWord(undefined);
|
||||||
}
|
}
|
||||||
|
|
21
src/index.js
21
src/index.js
|
@ -1,17 +1,16 @@
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from "react-dom";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
import "@fortawesome/fontawesome-free/css/all.css";
|
import "@fortawesome/fontawesome-free/css/all.css";
|
||||||
import 'bootstrap/dist/css/bootstrap.css';
|
import "bootstrap/dist/css/bootstrap.css";
|
||||||
import './App.css';
|
import "./App.css";
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
// <React.StrictMode>
|
||||||
<App />
|
<App />, // </React.StrictMode>,
|
||||||
</React.StrictMode>,
|
document.getElementById("root")
|
||||||
document.getElementById('root')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/inflect",
|
"name": "@lingdocs/inflect",
|
||||||
"version": "7.2.2",
|
"version": "7.3.0",
|
||||||
"description": "Pashto inflector library",
|
"description": "Pashto inflector library",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/lib/library.d.ts",
|
"types": "dist/lib/library.d.ts",
|
||||||
|
|
|
@ -81,6 +81,6 @@ export function makeNounSelection(
|
||||||
possesor: !complementType ? old?.possesor : undefined,
|
possesor: !complementType ? old?.possesor : undefined,
|
||||||
dynamicComplement: complementType === "dynamic",
|
dynamicComplement: complementType === "dynamic",
|
||||||
genStativeComplement: complementType === "generative stative",
|
genStativeComplement: complementType === "generative stative",
|
||||||
demonstrative: undefined,
|
demonstrative: old?.demonstrative,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,19 +12,26 @@ function getBaseAndAdjectives({
|
||||||
return getSandwichPsBaseAndAdjectives(selection);
|
return getSandwichPsBaseAndAdjectives(selection);
|
||||||
}
|
}
|
||||||
const adjs = "adjectives" in selection && selection.adjectives;
|
const adjs = "adjectives" in selection && selection.adjectives;
|
||||||
|
const demons = ("demonstrative" in selection &&
|
||||||
|
selection.demonstrative?.ps) || { p: "", f: "" };
|
||||||
if (!adjs) {
|
if (!adjs) {
|
||||||
return flattenLengths(selection.ps);
|
// TODO: does this ever get used??
|
||||||
|
return flattenLengths(selection.ps).map((x) => concatPsString(demons, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
return flattenLengths(selection.ps).map((p) =>
|
return flattenLengths(selection.ps).map((p) =>
|
||||||
concatPsString(
|
concatPsString(
|
||||||
|
demons,
|
||||||
|
// demons ? " " : "",
|
||||||
adjs.reduce(
|
adjs.reduce(
|
||||||
(accum, curr) =>
|
(accum, curr) => {
|
||||||
// TODO: with variations of adjs?
|
// TODO: with variations of adjs? {
|
||||||
concatPsString(
|
return concatPsString(
|
||||||
accum,
|
accum,
|
||||||
accum.p === "" && accum.f === "" ? "" : " ",
|
accum.p === "" && accum.f === "" ? "" : "", //" ",
|
||||||
curr.ps[0]
|
curr.ps[0]
|
||||||
),
|
);
|
||||||
|
},
|
||||||
{ p: "", f: "" }
|
{ p: "", f: "" }
|
||||||
),
|
),
|
||||||
" ",
|
" ",
|
||||||
|
@ -199,7 +206,10 @@ function addArticlesAndAdjs(
|
||||||
? " (f.)"
|
? " (f.)"
|
||||||
: " (m.)"
|
: " (m.)"
|
||||||
: "";
|
: "";
|
||||||
return `${articles}${adjs}${word}${genderTag}`;
|
const demonstrative = np.demonstrative ? ` ${np.demonstrative.e}` : "";
|
||||||
|
return `${
|
||||||
|
np.demonstrative ? "" : articles
|
||||||
|
}${demonstrative}${adjs}${word}${genderTag}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,34 +131,61 @@ export function renderNounSelection(
|
||||||
ps: pashto,
|
ps: pashto,
|
||||||
e: english,
|
e: english,
|
||||||
possesor: renderPossesor(n.possesor, role),
|
possesor: renderPossesor(n.possesor, role),
|
||||||
demonstrative: renderDemonstrative(
|
demonstrative: renderDemonstrative({
|
||||||
n.demonstrative,
|
demonstrative: n.demonstrative,
|
||||||
inflected && n.number === "plural"
|
inflected,
|
||||||
),
|
plural: n.number === "plural",
|
||||||
|
gender: n.gender,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDemonstrative(
|
function renderDemonstrative({
|
||||||
demonstrative: T.DemonstrativeSelection | undefined,
|
demonstrative,
|
||||||
plurInflected: boolean
|
inflected,
|
||||||
): T.Rendered<T.DemonstrativeSelection> | undefined {
|
plural,
|
||||||
|
gender,
|
||||||
|
}: {
|
||||||
|
demonstrative: T.DemonstrativeSelection | undefined;
|
||||||
|
inflected: boolean;
|
||||||
|
plural: boolean;
|
||||||
|
gender: T.Gender;
|
||||||
|
}): T.Rendered<T.DemonstrativeSelection> | undefined {
|
||||||
if (!demonstrative) {
|
if (!demonstrative) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
const ps =
|
||||||
|
demonstrative.demonstrative === "daa"
|
||||||
|
? inflected
|
||||||
|
? { p: "دې", f: "de" }
|
||||||
|
: { p: "دا", f: "daa" }
|
||||||
|
: demonstrative.demonstrative === "dagha"
|
||||||
|
? inflected
|
||||||
|
? plural
|
||||||
|
? { p: "دغو", f: "dágho" }
|
||||||
|
: gender === "masc"
|
||||||
|
? { p: "دغه", f: "dághu" }
|
||||||
|
: { p: "دغې", f: "dághe" }
|
||||||
|
: { p: "دغه", f: "dágha" }
|
||||||
|
: inflected
|
||||||
|
? plural
|
||||||
|
? { p: "هغو", f: "hágho" }
|
||||||
|
: gender === "masc"
|
||||||
|
? { p: "هغه", f: "hághu" }
|
||||||
|
: { p: "هغې", f: "hághe" }
|
||||||
|
: { p: "هغه", f: "hágha" };
|
||||||
|
const e =
|
||||||
|
demonstrative.demonstrative === "hagha"
|
||||||
|
? plural
|
||||||
|
? "those"
|
||||||
|
: "that"
|
||||||
|
: plural
|
||||||
|
? "these"
|
||||||
|
: "this";
|
||||||
return {
|
return {
|
||||||
...demonstrative,
|
...demonstrative,
|
||||||
ps:
|
ps,
|
||||||
demonstrative.demonstrative === "daa"
|
e,
|
||||||
? plurInflected
|
|
||||||
? { p: "دې", f: "de" }
|
|
||||||
: { p: "دا", f: "daa" }
|
|
||||||
: demonstrative.demonstrative === "dagha"
|
|
||||||
? plurInflected
|
|
||||||
? { p: "دغه", f: "dágha" }
|
|
||||||
: { p: "دغو", f: "dágho" }
|
|
||||||
: plurInflected
|
|
||||||
? { p: "هغه", f: "hágha" }
|
|
||||||
: { p: "هغو", f: "hágho" },
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,333 +1,364 @@
|
||||||
import * as T from "../../../types";
|
import * as T from "../../../types";
|
||||||
import {
|
import { isInvalidSubjObjCombo } from "./vp-tools";
|
||||||
isInvalidSubjObjCombo,
|
|
||||||
} from "./vp-tools";
|
|
||||||
import { switchSubjObj } from "./vp-tools";
|
import { switchSubjObj } from "./vp-tools";
|
||||||
import { ensure2ndPersSubjPronounAndNoConflict } from "./vp-tools";
|
import { ensure2ndPersSubjPronounAndNoConflict } from "./vp-tools";
|
||||||
import {
|
import { isPerfectTense, isImperativeTense } from "../type-predicates";
|
||||||
isPerfectTense,
|
|
||||||
isImperativeTense,
|
|
||||||
} from "../type-predicates";
|
|
||||||
import { checkForMiniPronounsError } from "./compile";
|
import { checkForMiniPronounsError } from "./compile";
|
||||||
import {
|
import {
|
||||||
adjustObjectSelection,
|
adjustObjectSelection,
|
||||||
adjustSubjectSelection,
|
adjustSubjectSelection,
|
||||||
getObjectSelection,
|
getObjectSelection,
|
||||||
getSubjectSelection,
|
getSubjectSelection,
|
||||||
insertNewAP,
|
insertNewAP,
|
||||||
removeAP,
|
removeAP,
|
||||||
setAP,
|
setAP,
|
||||||
shiftBlock,
|
shiftBlock,
|
||||||
} from "./blocks-utils";
|
} from "./blocks-utils";
|
||||||
import { changeStatDyn, changeTransitivity, makeVPSelectionState } from "./verb-selection";
|
import {
|
||||||
|
changeStatDyn,
|
||||||
|
changeTransitivity,
|
||||||
|
makeVPSelectionState,
|
||||||
|
} from "./verb-selection";
|
||||||
|
|
||||||
export type VpsReducerAction = {
|
export type VpsReducerAction =
|
||||||
type: "load vps",
|
| {
|
||||||
payload: T.VPSelectionState,
|
type: "load vps";
|
||||||
} | {
|
payload: T.VPSelectionState;
|
||||||
type: "set subject",
|
|
||||||
payload: {
|
|
||||||
subject: T.NPSelection | undefined,
|
|
||||||
skipPronounConflictCheck?: boolean,
|
|
||||||
},
|
|
||||||
} | {
|
|
||||||
type: "set object",
|
|
||||||
payload: T.NPSelection | undefined,
|
|
||||||
} | {
|
|
||||||
type: "swap subj/obj",
|
|
||||||
} | {
|
|
||||||
type: "set form",
|
|
||||||
payload: T.FormVersion,
|
|
||||||
} | {
|
|
||||||
type: "set voice",
|
|
||||||
payload: "active" | "passive",
|
|
||||||
} | {
|
|
||||||
type: "set transitivity",
|
|
||||||
payload: "transitive" | "grammatically transitive",
|
|
||||||
} | {
|
|
||||||
type: "set statDyn",
|
|
||||||
payload: "stative" | "dynamic",
|
|
||||||
} | {
|
|
||||||
type: "set negativity",
|
|
||||||
payload: "true" | "false",
|
|
||||||
} | {
|
|
||||||
type: "set tense",
|
|
||||||
payload: T.VerbTense | T.PerfectTense | T.ImperativeTense | undefined,
|
|
||||||
} | {
|
|
||||||
type: "set tense category",
|
|
||||||
payload: "basic" | "modal" | "perfect" | "imperative",
|
|
||||||
} | {
|
|
||||||
type: "toggle servant shrink",
|
|
||||||
} | {
|
|
||||||
type: "toggle king remove",
|
|
||||||
} |{
|
|
||||||
type: "set verb",
|
|
||||||
payload: T.VerbEntry,
|
|
||||||
} | {
|
|
||||||
type: "insert new AP",
|
|
||||||
} | {
|
|
||||||
type: "set AP",
|
|
||||||
payload: {
|
|
||||||
index: number,
|
|
||||||
AP: T.APSelection | undefined,
|
|
||||||
},
|
|
||||||
} | {
|
|
||||||
type: "remove AP",
|
|
||||||
payload: number,
|
|
||||||
} | {
|
|
||||||
type: "shift block",
|
|
||||||
payload: {
|
|
||||||
index: number,
|
|
||||||
direction: "back" | "forward",
|
|
||||||
},
|
|
||||||
} | {
|
|
||||||
type: "set externalComplement",
|
|
||||||
payload: T.ComplementSelection | undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
export function vpsReducer(vps: T.VPSelectionState, action: VpsReducerAction, sendAlert?: (msg: string) => void): T.VPSelectionState {
|
|
||||||
function doReduce(): T.VPSelectionState {
|
|
||||||
if (action.type === "load vps") {
|
|
||||||
return action.payload;
|
|
||||||
}
|
|
||||||
if (action.type === "set subject") {
|
|
||||||
const { subject, skipPronounConflictCheck } = action.payload;
|
|
||||||
const object = getObjectSelection(vps.blocks).selection;
|
|
||||||
if (
|
|
||||||
!skipPronounConflictCheck
|
|
||||||
&&
|
|
||||||
hasPronounConflict(subject, object)
|
|
||||||
) {
|
|
||||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
|
||||||
return vps;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: adjustSubjectSelection(vps.blocks, action.payload.subject),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set object") {
|
|
||||||
if (!vps.verb) return vps;
|
|
||||||
const objectB = getObjectSelection(vps.blocks).selection;
|
|
||||||
const subjectB = getSubjectSelection(vps.blocks).selection;
|
|
||||||
if ((objectB === "none") || (typeof objectB === "number")) {
|
|
||||||
return vps;
|
|
||||||
}
|
|
||||||
const object = action.payload;
|
|
||||||
// check for pronoun conflict
|
|
||||||
if (hasPronounConflict(subjectB, object)) {
|
|
||||||
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
|
||||||
return vps;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: adjustObjectSelection(vps.blocks, object),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "swap subj/obj") {
|
|
||||||
if (vps.verb?.isCompound === "dynamic") return vps;
|
|
||||||
return switchSubjObj(vps);
|
|
||||||
}
|
|
||||||
if (action.type === "set form") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
form: action.payload,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set voice") {
|
|
||||||
if (vps.verb && vps.verb.canChangeVoice) {
|
|
||||||
const subject = getSubjectSelection(vps.blocks).selection;
|
|
||||||
const object = getObjectSelection(vps.blocks).selection;
|
|
||||||
const voice = action.payload;
|
|
||||||
if (voice === "passive" && vps.verb.tenseCategory === "imperative") {
|
|
||||||
return vps;
|
|
||||||
}
|
|
||||||
if (voice === "passive") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: adjustObjectSelection(
|
|
||||||
adjustSubjectSelection(vps.blocks, typeof object === "object" ? object : undefined),
|
|
||||||
"none",
|
|
||||||
),
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
voice,
|
|
||||||
// tenseCategory: vps.verb.tenseCategory === "modal" ? "basic" : vps.verb.tenseCategory,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: adjustObjectSelection(
|
|
||||||
adjustSubjectSelection(vps.blocks, undefined),
|
|
||||||
typeof subject === "object" ? subject : undefined,
|
|
||||||
),
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
voice,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return vps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action.type === "set transitivity") {
|
|
||||||
if (!(vps.verb && vps.verb.canChangeTransitivity)) return vps;
|
|
||||||
return changeTransitivity(vps, action.payload);
|
|
||||||
}
|
|
||||||
if (action.type === "set statDyn") {
|
|
||||||
if (!(vps.verb && vps.verb.canChangeStatDyn)) return vps;
|
|
||||||
return changeStatDyn(vps, action.payload);
|
|
||||||
}
|
|
||||||
if (action.type === "set negativity") {
|
|
||||||
if (!vps.verb) return vps;
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
negative: action.payload === "true",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set tense") {
|
|
||||||
const tense = action.payload;
|
|
||||||
if (!(vps.verb && tense)) return vps;
|
|
||||||
if (isPerfectTense(tense)) {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
perfectTense: tense,
|
|
||||||
tenseCategory: "perfect",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else if (isImperativeTense(tense)) {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
imperativeTense: tense,
|
|
||||||
tenseCategory: "imperative",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
verbTense: tense,
|
|
||||||
tenseCategory: vps.verb.tenseCategory === "perfect"
|
|
||||||
? "basic"
|
|
||||||
: vps.verb.tenseCategory,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (action.type === "set tense category") {
|
|
||||||
if (!vps.verb) return vps;
|
|
||||||
const category = action.payload;
|
|
||||||
if (category === "imperative") {
|
|
||||||
return ensure2ndPersSubjPronounAndNoConflict({
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
voice: "active",
|
|
||||||
tenseCategory: category,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (category === "modal") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
tenseCategory: category,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
verb: {
|
|
||||||
...vps.verb,
|
|
||||||
tenseCategory: category,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "toggle servant shrink") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
form: {
|
|
||||||
...vps.form,
|
|
||||||
shrinkServant: !vps.form.shrinkServant,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "toggle king remove") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
form: {
|
|
||||||
...vps.form,
|
|
||||||
removeKing: !vps.form.removeKing,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set verb") {
|
|
||||||
return makeVPSelectionState(action.payload, vps);
|
|
||||||
}
|
|
||||||
if (action.type === "insert new AP") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: insertNewAP(vps.blocks),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set AP") {
|
|
||||||
const { index, AP } = action.payload;
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: setAP(vps.blocks, index, AP),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "remove AP") {
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: removeAP(vps.blocks, action.payload),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "shift block") {
|
|
||||||
const { index, direction } = action.payload;
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
blocks: shiftBlock(vps.blocks, index, direction),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (action.type === "set externalComplement") {
|
|
||||||
const selection = action.payload;
|
|
||||||
return {
|
|
||||||
...vps,
|
|
||||||
externalComplement: selection === undefined
|
|
||||||
// TODO: this is a bit messy
|
|
||||||
// when using the ComplementPicker with an EP - undefined means it hasn't been selected
|
|
||||||
// when using the ComplementPicker with a VP - undefined means there can be no complement
|
|
||||||
? { type: "complement", selection: { type: "unselected" }}
|
|
||||||
: selection,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error("unknown vpsReducer state");
|
|
||||||
}
|
}
|
||||||
const modified = doReduce();
|
| {
|
||||||
const err = checkForMiniPronounsError(modified);
|
type: "set subject";
|
||||||
if (err) {
|
payload: {
|
||||||
if (sendAlert) sendAlert(err);
|
subject: T.NPSelection | undefined;
|
||||||
|
skipPronounConflictCheck?: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set object";
|
||||||
|
payload: T.NPSelection | undefined;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "swap subj/obj";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set form";
|
||||||
|
payload: T.FormVersion;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set voice";
|
||||||
|
payload: "active" | "passive";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set transitivity";
|
||||||
|
payload: "transitive" | "grammatically transitive";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set statDyn";
|
||||||
|
payload: "stative" | "dynamic";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set negativity";
|
||||||
|
payload: "true" | "false";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set tense";
|
||||||
|
payload: T.VerbTense | T.PerfectTense | T.ImperativeTense | undefined;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set tense category";
|
||||||
|
payload: "basic" | "modal" | "perfect" | "imperative";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "toggle servant shrink";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "toggle king remove";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set verb";
|
||||||
|
payload: T.VerbEntry;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "insert new AP";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set AP";
|
||||||
|
payload: {
|
||||||
|
index: number;
|
||||||
|
AP: T.APSelection | undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "remove AP";
|
||||||
|
payload: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "shift block";
|
||||||
|
payload: {
|
||||||
|
index: number;
|
||||||
|
direction: "back" | "forward";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: "set externalComplement";
|
||||||
|
payload: T.ComplementSelection | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function vpsReducer(
|
||||||
|
vps: T.VPSelectionState,
|
||||||
|
action: VpsReducerAction,
|
||||||
|
sendAlert?: (msg: string) => void
|
||||||
|
): T.VPSelectionState {
|
||||||
|
function doReduce(): T.VPSelectionState {
|
||||||
|
if (action.type === "load vps") {
|
||||||
|
return action.payload;
|
||||||
|
}
|
||||||
|
if (action.type === "set subject") {
|
||||||
|
const { subject, skipPronounConflictCheck } = action.payload;
|
||||||
|
const object = getObjectSelection(vps.blocks).selection;
|
||||||
|
if (!skipPronounConflictCheck && hasPronounConflict(subject, object)) {
|
||||||
|
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||||
return vps;
|
return vps;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: adjustSubjectSelection(vps.blocks, action.payload.subject),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return modified;
|
if (action.type === "set object") {
|
||||||
|
if (!vps.verb) return vps;
|
||||||
|
const objectB = getObjectSelection(vps.blocks).selection;
|
||||||
|
const subjectB = getSubjectSelection(vps.blocks).selection;
|
||||||
|
if (objectB === "none" || typeof objectB === "number") {
|
||||||
|
return vps;
|
||||||
|
}
|
||||||
|
const object = action.payload;
|
||||||
|
// check for pronoun conflict
|
||||||
|
if (hasPronounConflict(subjectB, object)) {
|
||||||
|
if (sendAlert) sendAlert("That combination of pronouns is not allowed");
|
||||||
|
return vps;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: adjustObjectSelection(vps.blocks, object),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "swap subj/obj") {
|
||||||
|
if (vps.verb?.isCompound === "dynamic") return vps;
|
||||||
|
return switchSubjObj(vps);
|
||||||
|
}
|
||||||
|
if (action.type === "set form") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
form: action.payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "set voice") {
|
||||||
|
if (vps.verb && vps.verb.canChangeVoice) {
|
||||||
|
const subject = getSubjectSelection(vps.blocks).selection;
|
||||||
|
const object = getObjectSelection(vps.blocks).selection;
|
||||||
|
const voice = action.payload;
|
||||||
|
if (voice === "passive" && vps.verb.tenseCategory === "imperative") {
|
||||||
|
return vps;
|
||||||
|
}
|
||||||
|
if (voice === "passive") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: adjustObjectSelection(
|
||||||
|
adjustSubjectSelection(
|
||||||
|
vps.blocks,
|
||||||
|
typeof object === "object" ? object : undefined
|
||||||
|
),
|
||||||
|
"none"
|
||||||
|
),
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
voice,
|
||||||
|
// tenseCategory: vps.verb.tenseCategory === "modal" ? "basic" : vps.verb.tenseCategory,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: adjustObjectSelection(
|
||||||
|
adjustSubjectSelection(vps.blocks, undefined),
|
||||||
|
typeof subject === "object" ? subject : undefined
|
||||||
|
),
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
voice,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return vps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action.type === "set transitivity") {
|
||||||
|
if (!(vps.verb && vps.verb.canChangeTransitivity)) return vps;
|
||||||
|
return changeTransitivity(vps, action.payload);
|
||||||
|
}
|
||||||
|
if (action.type === "set statDyn") {
|
||||||
|
if (!(vps.verb && vps.verb.canChangeStatDyn)) return vps;
|
||||||
|
return changeStatDyn(vps, action.payload);
|
||||||
|
}
|
||||||
|
if (action.type === "set negativity") {
|
||||||
|
if (!vps.verb) return vps;
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
negative: action.payload === "true",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "set tense") {
|
||||||
|
const tense = action.payload;
|
||||||
|
if (!(vps.verb && tense)) return vps;
|
||||||
|
if (isPerfectTense(tense)) {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
perfectTense: tense,
|
||||||
|
tenseCategory: "perfect",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (isImperativeTense(tense)) {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
imperativeTense: tense,
|
||||||
|
tenseCategory: "imperative",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
verbTense: tense,
|
||||||
|
tenseCategory:
|
||||||
|
vps.verb.tenseCategory === "perfect"
|
||||||
|
? "basic"
|
||||||
|
: vps.verb.tenseCategory,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action.type === "set tense category") {
|
||||||
|
if (!vps.verb) return vps;
|
||||||
|
const category = action.payload;
|
||||||
|
if (category === "imperative") {
|
||||||
|
return ensure2ndPersSubjPronounAndNoConflict({
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
voice: "active",
|
||||||
|
tenseCategory: category,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (category === "modal") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
tenseCategory: category,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
verb: {
|
||||||
|
...vps.verb,
|
||||||
|
tenseCategory: category,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "toggle servant shrink") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
form: {
|
||||||
|
...vps.form,
|
||||||
|
shrinkServant: !vps.form.shrinkServant,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "toggle king remove") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
form: {
|
||||||
|
...vps.form,
|
||||||
|
removeKing: !vps.form.removeKing,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "set verb") {
|
||||||
|
return makeVPSelectionState(action.payload, vps);
|
||||||
|
}
|
||||||
|
if (action.type === "insert new AP") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: insertNewAP(vps.blocks),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "set AP") {
|
||||||
|
const { index, AP } = action.payload;
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: setAP(vps.blocks, index, AP),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "remove AP") {
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: removeAP(vps.blocks, action.payload),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "shift block") {
|
||||||
|
const { index, direction } = action.payload;
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
blocks: shiftBlock(vps.blocks, index, direction),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (action.type === "set externalComplement") {
|
||||||
|
const selection = action.payload;
|
||||||
|
return {
|
||||||
|
...vps,
|
||||||
|
externalComplement:
|
||||||
|
selection === undefined
|
||||||
|
? // TODO: this is a bit messy
|
||||||
|
// when using the ComplementPicker with an EP - undefined means it hasn't been selected
|
||||||
|
// when using the ComplementPicker with a VP - undefined means there can be no complement
|
||||||
|
{ type: "complement", selection: { type: "unselected" } }
|
||||||
|
: selection,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new Error("unknown vpsReducer state");
|
||||||
|
}
|
||||||
|
const modified = doReduce();
|
||||||
|
const err = checkForMiniPronounsError(modified);
|
||||||
|
if (err) {
|
||||||
|
if (sendAlert) sendAlert(err);
|
||||||
|
return vps;
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPronounConflict(subject: T.NPSelection | undefined, object: undefined | T.VerbObject): boolean {
|
function hasPronounConflict(
|
||||||
const subjPronoun = (subject && subject.selection.type === "pronoun") ? subject.selection : undefined;
|
subject: T.NPSelection | undefined,
|
||||||
const objPronoun = (object && typeof object === "object" && object.selection.type === "pronoun") ? object.selection : undefined;
|
object: undefined | T.VerbObject
|
||||||
if (!subjPronoun || !objPronoun) return false;
|
): boolean {
|
||||||
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
|
const subjPronoun =
|
||||||
|
subject && subject.selection.type === "pronoun"
|
||||||
|
? subject.selection
|
||||||
|
: undefined;
|
||||||
|
const objPronoun =
|
||||||
|
object && typeof object === "object" && object.selection.type === "pronoun"
|
||||||
|
? object.selection
|
||||||
|
: undefined;
|
||||||
|
if (!subjPronoun || !objPronoun) return false;
|
||||||
|
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1026,6 +1026,7 @@ export type Rendered<
|
||||||
demonstrative: DemonstrativeSelection["demonstrative"];
|
demonstrative: DemonstrativeSelection["demonstrative"];
|
||||||
hideNoun: boolean;
|
hideNoun: boolean;
|
||||||
ps: PsString;
|
ps: PsString;
|
||||||
|
e: string;
|
||||||
}
|
}
|
||||||
: T extends ComplementSelection
|
: T extends ComplementSelection
|
||||||
? {
|
? {
|
||||||
|
|
Loading…
Reference in New Issue