This commit is contained in:
parent
acd7b7b504
commit
346a89df29
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "3.0.5",
|
"version": "3.0.6",
|
||||||
"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",
|
||||||
|
|
|
@ -177,7 +177,7 @@ function TensePicker(props: ({
|
||||||
: verbTenseOptions;
|
: verbTenseOptions;
|
||||||
const showImperativeOption = ("vps" in props && props.vps.verb.voice === "active")
|
const showImperativeOption = ("vps" in props && props.vps.verb.voice === "active")
|
||||||
|| ("vpsComplete" in props && props.vpsComplete.verb.voice !== "active");
|
|| ("vpsComplete" in props && props.vpsComplete.verb.voice !== "active");
|
||||||
const canHaveFormula = "vps" in props;
|
const canHaveFormula = "vps" in props && props.mode !== "quiz";
|
||||||
return <div>
|
return <div>
|
||||||
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
<div style={{ maxWidth: "300px", minWidth: "250px", margin: "0 auto" }}>
|
||||||
<div className="d-flex flex-row justify-content-between align-items-center">
|
<div className="d-flex flex-row justify-content-between align-items-center">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import NPPicker, { shrunkenBackground } from "../np-picker/NPPicker";
|
|
||||||
import VerbPicker from "./VerbPicker";
|
import VerbPicker from "./VerbPicker";
|
||||||
import TensePicker from "./TensePicker";
|
import TensePicker from "./TensePicker";
|
||||||
import VPDisplay from "./VPDisplay";
|
import VPDisplay from "./VPDisplay";
|
||||||
|
@ -7,17 +6,14 @@ import * as T from "../../types";
|
||||||
import ChartDisplay from "./VPChartDisplay";
|
import ChartDisplay from "./VPChartDisplay";
|
||||||
import useStickyState, { useStickyReducer } from "../../lib/useStickyState";
|
import useStickyState, { useStickyReducer } from "../../lib/useStickyState";
|
||||||
import { makeVPSelectionState } from "./verb-selection";
|
import { makeVPSelectionState } from "./verb-selection";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { getKingAndServant, renderVP } from "../../lib/phrase-building/render-vp";
|
import { completeVPSelection } from "../../lib/phrase-building/vp-tools";
|
||||||
import { completeVPSelection, isPastTense } from "../../lib/phrase-building/vp-tools";
|
|
||||||
import VPExplorerQuiz from "./VPExplorerQuiz";
|
import VPExplorerQuiz from "./VPExplorerQuiz";
|
||||||
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import LZString from "lz-string";
|
import LZString from "lz-string";
|
||||||
import { vpsReducer } from "./vps-reducer";
|
import { vpsReducer } from "./vps-reducer";
|
||||||
import APPicker from "../ap-picker/APPicker";
|
import { getObjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||||
import autoAnimate from "@formkit/auto-animate";
|
import VPPicker from "./VPPicker";
|
||||||
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
|
||||||
|
|
||||||
const phraseURLParam = "VPhrase";
|
const phraseURLParam = "VPhrase";
|
||||||
|
|
||||||
|
@ -56,16 +52,6 @@ function VPExplorer(props: {
|
||||||
);
|
);
|
||||||
const [showClipped, setShowClipped] = useState<string>("");
|
const [showClipped, setShowClipped] = useState<string>("");
|
||||||
const [alert, setAlert] = useState<string | undefined>(undefined);
|
const [alert, setAlert] = useState<string | undefined>(undefined);
|
||||||
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
|
|
||||||
const parent = useRef<HTMLDivElement>(null);
|
|
||||||
useEffect(() => {
|
|
||||||
parent.current && autoAnimate(parent.current);
|
|
||||||
}, [parent]);
|
|
||||||
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
|
|
||||||
const roles = getKingAndServant(
|
|
||||||
isPast,
|
|
||||||
vps.verb.transitivity !== "intransitive",
|
|
||||||
);
|
|
||||||
function flashMessage(msg: string) {
|
function flashMessage(msg: string) {
|
||||||
setAlert(msg);
|
setAlert(msg);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -90,18 +76,6 @@ function VPExplorer(props: {
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
|
|
||||||
adjustVps({
|
|
||||||
type: "set subject",
|
|
||||||
payload: { subject, skipPronounConflictCheck },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function handleObjectChange(object: T.NPSelection | undefined) {
|
|
||||||
adjustVps({
|
|
||||||
type: "set object",
|
|
||||||
payload: object,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function handleSubjObjSwap() {
|
function handleSubjObjSwap() {
|
||||||
adjustVps({ type: "swap subj/obj" });
|
adjustVps({ type: "swap subj/obj" });
|
||||||
}
|
}
|
||||||
|
@ -138,16 +112,8 @@ function VPExplorer(props: {
|
||||||
flashClippedMessage("Copied phrase code to clipboard");
|
flashClippedMessage("Copied phrase code to clipboard");
|
||||||
}
|
}
|
||||||
const object = getObjectSelection(vps.blocks).selection;
|
const object = getObjectSelection(vps.blocks).selection;
|
||||||
const subject = getSubjectSelection(vps.blocks).selection;
|
|
||||||
const VPS = completeVPSelection(vps);
|
const VPS = completeVPSelection(vps);
|
||||||
const phraseIsComplete = !!VPS;
|
const phraseIsComplete = !!VPS;
|
||||||
const rendered = VPS ? renderVP(VPS) : undefined;
|
|
||||||
const servantIsShrunk = includesShrunkenServant(rendered?.kids);
|
|
||||||
function toggleServantShrink() {
|
|
||||||
adjustVps({
|
|
||||||
type: "toggle servant shrink",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
||||||
<VerbPicker
|
<VerbPicker
|
||||||
vps={vps}
|
vps={vps}
|
||||||
|
@ -190,113 +156,18 @@ function VPExplorer(props: {
|
||||||
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
<i className="fas fa-exchange-alt mr-2" /> subj/obj
|
||||||
</button>
|
</button>
|
||||||
</div>}
|
</div>}
|
||||||
{mode === "phrases" && <div className="clickable h5" onClick={() => adjustVps({ type: "insert new AP" })}>+ AP</div>}
|
{mode === "phrases" && <VPPicker
|
||||||
{mode !== "quiz" && <div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
|
|
||||||
{mode === "phrases" && <>
|
|
||||||
{vps.blocks.map(({ block, key }, i, blocks) => {
|
|
||||||
if (isNoObject(block)) return null;
|
|
||||||
return <div className="my-2 card block-card p-1 mx-1" key={key} style={{
|
|
||||||
background: (servantIsShrunk && (
|
|
||||||
(roles.servant === "subject" && block?.type === "subjectSelection")
|
|
||||||
||
|
|
||||||
(roles.servant === "object" && block?.type === "objectSelection")
|
|
||||||
)) ? shrunkenBackground : "inherit",
|
|
||||||
}}>
|
|
||||||
<div className="d-flex flex-row justify-content-between mb-1" style={{ height: "1rem" }}>
|
|
||||||
{(i > 0 && !isNoObject(blocks[i - 1].block)) ? <div
|
|
||||||
className="small clickable ml-1"
|
|
||||||
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "back" }})}
|
|
||||||
>
|
|
||||||
<i className="fas fa-chevron-left" />
|
|
||||||
</div> : <div/>}
|
|
||||||
{(i < vps.blocks.length - 1 && !isNoObject(blocks[i + 1].block)) ? <div
|
|
||||||
className="small clickable mr-1"
|
|
||||||
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "forward" }})}
|
|
||||||
>
|
|
||||||
<i className="fas fa-chevron-right" />
|
|
||||||
</div> : <div/>}
|
|
||||||
</div>
|
|
||||||
{(!block || block.type === "AP")
|
|
||||||
? <APPicker
|
|
||||||
phraseIsComplete={phraseIsComplete}
|
|
||||||
heading="AP"
|
|
||||||
entryFeeder={props.entryFeeder}
|
|
||||||
AP={block}
|
|
||||||
opts={props.opts}
|
opts={props.opts}
|
||||||
onChange={AP => adjustVps({ type: "set AP", payload: { index: i, AP } })}
|
|
||||||
onRemove={() => adjustVps({ type: "remove AP", payload: i })}
|
|
||||||
/>
|
|
||||||
: (block?.type === "subjectSelection")
|
|
||||||
? <NPPicker
|
|
||||||
phraseIsComplete={phraseIsComplete}
|
|
||||||
heading={roles.king === "subject"
|
|
||||||
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
|
||||||
: <div className="h5 text-center">
|
|
||||||
Subject
|
|
||||||
{` `}
|
|
||||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
|
|
||||||
{` `}
|
|
||||||
{(rendered && rendered.whatsAdjustable !== "king") &&
|
|
||||||
<span onClick={toggleServantShrink} className="mx-2 clickable">
|
|
||||||
{!servantIsShrunk ? "🪄" : "👶"}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>}
|
|
||||||
entryFeeder={props.entryFeeder}
|
entryFeeder={props.entryFeeder}
|
||||||
np={block.selection}
|
onChange={(vps) => adjustVps({ type: "load vps", payload: vps })}
|
||||||
counterPart={vps.verb ? object : undefined}
|
vps={vps}
|
||||||
role={(isPast && vps.verb.transitivity !== "intransitive")
|
|
||||||
? "ergative"
|
|
||||||
: "subject"
|
|
||||||
}
|
|
||||||
onChange={handleSubjectChange}
|
|
||||||
opts={props.opts}
|
|
||||||
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
|
||||||
/>
|
|
||||||
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
|
||||||
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
|
||||||
{(typeof block.selection === "number")
|
|
||||||
? <div>
|
|
||||||
{roles.king === "object"
|
|
||||||
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
|
||||||
: <div className="h5 text-center">Object</div>}
|
|
||||||
<div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
|
||||||
</div>
|
|
||||||
: <NPPicker
|
|
||||||
phraseIsComplete={phraseIsComplete}
|
|
||||||
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">
|
|
||||||
Object
|
|
||||||
{` `}
|
|
||||||
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
|
|
||||||
{` `}
|
|
||||||
{(rendered && rendered.whatsAdjustable !== "king") &&
|
|
||||||
<span onClick={toggleServantShrink} className="mx-2 clickable">
|
|
||||||
{!servantIsShrunk ? "🪄" : "👶"}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
</div>}
|
|
||||||
entryFeeder={props.entryFeeder}
|
|
||||||
role="object"
|
|
||||||
np={block.selection}
|
|
||||||
counterPart={subject}
|
|
||||||
onChange={handleObjectChange}
|
|
||||||
opts={props.opts}
|
|
||||||
isShrunk={(servantIsShrunk && roles.servant === "object")}
|
|
||||||
/>}
|
/>}
|
||||||
</div>
|
{mode !== "phrases" && <div className="my-2">
|
||||||
: null}
|
|
||||||
</div>;
|
|
||||||
})}
|
|
||||||
</>}
|
|
||||||
<div className="my-2">
|
|
||||||
<TensePicker
|
<TensePicker
|
||||||
vps={vps}
|
vps={vps}
|
||||||
onChange={quizLock(adjustVps)}
|
onChange={adjustVps}
|
||||||
mode={mode}
|
mode={mode}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>}
|
</div>}
|
||||||
{mode === "phrases" && <VPDisplay
|
{mode === "phrases" && <VPDisplay
|
||||||
VPS={vps}
|
VPS={vps}
|
||||||
|
@ -305,10 +176,6 @@ function VPExplorer(props: {
|
||||||
/>}
|
/>}
|
||||||
{mode === "charts" && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
{mode === "charts" && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
||||||
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
|
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
|
||||||
<VPExplorerExplanationModal
|
|
||||||
showing={showingExplanation}
|
|
||||||
setShowing={setShowingExplanation}
|
|
||||||
/>
|
|
||||||
{showClipped && <div className="alert alert-primary text-center" role="alert" style={{
|
{showClipped && <div className="alert alert-primary text-center" role="alert" style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
top: "30%",
|
top: "30%",
|
||||||
|
@ -355,10 +222,3 @@ function getVPSFromUrl(): T.VPSelectionState | undefined {
|
||||||
return JSON.parse(decoded) as T.VPSelectionState;
|
return JSON.parse(decoded) as T.VPSelectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
function includesShrunkenServant(kids?: T.Kid[]): boolean {
|
|
||||||
if (!kids) return false;
|
|
||||||
return kids.some(k => (
|
|
||||||
k.type === "mini-pronoun" && k.source === "servant"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
import NPPicker, { shrunkenBackground } from "../np-picker/NPPicker";
|
||||||
|
import TensePicker from "./TensePicker";
|
||||||
|
import * as T from "../../types";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { getKingAndServant, renderVP } from "../../lib/phrase-building/render-vp";
|
||||||
|
import { completeVPSelection, isPastTense } from "../../lib/phrase-building/vp-tools";
|
||||||
|
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
|
||||||
|
import { vpsReducer, VpsReducerAction } from "./vps-reducer";
|
||||||
|
import APPicker from "../ap-picker/APPicker";
|
||||||
|
import autoAnimate from "@formkit/auto-animate";
|
||||||
|
import { getObjectSelection, getSubjectSelection, includesShrunkenServant, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
||||||
|
|
||||||
|
function VPPicker({ opts, vps, onChange, entryFeeder }: {
|
||||||
|
opts: T.TextOptions,
|
||||||
|
vps: T.VPSelectionState,
|
||||||
|
onChange: (eps: T.VPSelectionState) => void,
|
||||||
|
entryFeeder: T.EntryFeeder,
|
||||||
|
}) {
|
||||||
|
const parent = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
parent.current && autoAnimate(parent.current);
|
||||||
|
}, [parent]);
|
||||||
|
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
|
||||||
|
const [alert, setAlert] = useState<string | undefined>(undefined);
|
||||||
|
function flashMessage(msg: string) {
|
||||||
|
setAlert(msg);
|
||||||
|
setTimeout(() => {
|
||||||
|
setAlert(undefined);
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
function adjustVps(action: VpsReducerAction) {
|
||||||
|
const newVps = vpsReducer(vps, action, flashMessage);
|
||||||
|
onChange(newVps);
|
||||||
|
}
|
||||||
|
function handleSubjectChange(subject: T.NPSelection | undefined, skipPronounConflictCheck?: boolean) {
|
||||||
|
adjustVps({
|
||||||
|
type: "set subject",
|
||||||
|
payload: { subject, skipPronounConflictCheck },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function handleObjectChange(object: T.NPSelection | undefined) {
|
||||||
|
adjustVps({
|
||||||
|
type: "set object",
|
||||||
|
payload: object,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const object = getObjectSelection(vps.blocks).selection;
|
||||||
|
const subject = getSubjectSelection(vps.blocks).selection;
|
||||||
|
const VPS = completeVPSelection(vps);
|
||||||
|
const phraseIsComplete = !!VPS;
|
||||||
|
const rendered = VPS ? renderVP(VPS) : undefined;
|
||||||
|
const servantIsShrunk = includesShrunkenServant(rendered?.kids);
|
||||||
|
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
|
||||||
|
const roles = getKingAndServant(
|
||||||
|
isPast,
|
||||||
|
vps.verb.transitivity !== "intransitive",
|
||||||
|
);
|
||||||
|
return <div>
|
||||||
|
<div className="clickable h5" onClick={() => adjustVps({ type: "insert new AP" })}>+ AP</div>
|
||||||
|
<div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
|
||||||
|
{vps.blocks.map(({ block, key }, i, blocks) => {
|
||||||
|
if (isNoObject(block)) return null;
|
||||||
|
return <div className="my-2 card block-card p-1 mx-1" key={key} style={{
|
||||||
|
background: (servantIsShrunk && (
|
||||||
|
(roles.servant === "subject" && block?.type === "subjectSelection")
|
||||||
|
||
|
||||||
|
(roles.servant === "object" && block?.type === "objectSelection")
|
||||||
|
)) ? shrunkenBackground : "inherit",
|
||||||
|
}}>
|
||||||
|
<div className="d-flex flex-row justify-content-between mb-1" style={{ height: "1rem" }}>
|
||||||
|
{(i > 0 && !isNoObject(blocks[i - 1].block)) ? <div
|
||||||
|
className="small clickable ml-1"
|
||||||
|
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "back" }})}
|
||||||
|
>
|
||||||
|
<i className="fas fa-chevron-left" />
|
||||||
|
</div> : <div/>}
|
||||||
|
{(i < vps.blocks.length - 1 && !isNoObject(blocks[i + 1].block)) ? <div
|
||||||
|
className="small clickable mr-1"
|
||||||
|
onClick={() => adjustVps({ type: "shift block", payload: { index: i, direction: "forward" }})}
|
||||||
|
>
|
||||||
|
<i className="fas fa-chevron-right" />
|
||||||
|
</div> : <div/>}
|
||||||
|
</div>
|
||||||
|
{(!block || block.type === "AP")
|
||||||
|
? <APPicker
|
||||||
|
phraseIsComplete={phraseIsComplete}
|
||||||
|
heading="AP"
|
||||||
|
entryFeeder={entryFeeder}
|
||||||
|
AP={block}
|
||||||
|
opts={opts}
|
||||||
|
onChange={AP => adjustVps({ type: "set AP", payload: { index: i, AP } })}
|
||||||
|
onRemove={() => adjustVps({ type: "remove AP", payload: i })}
|
||||||
|
/>
|
||||||
|
: (block?.type === "subjectSelection")
|
||||||
|
? <NPPicker
|
||||||
|
phraseIsComplete={phraseIsComplete}
|
||||||
|
heading={roles.king === "subject"
|
||||||
|
? <div className="h5 text-center" onClick={() => setShowingExplanation({ role: "king", item: "subject" })}>Subject {roleIcon.king}</div>
|
||||||
|
: <div className="h5 text-center">
|
||||||
|
Subject
|
||||||
|
{` `}
|
||||||
|
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "subject" })}>{roleIcon.servant}</span>
|
||||||
|
{` `}
|
||||||
|
{(rendered && rendered.whatsAdjustable !== "king") &&
|
||||||
|
<span onClick={() => adjustVps({ type: "toggle servant shrink" })} className="mx-2 clickable">
|
||||||
|
{!servantIsShrunk ? "🪄" : "👶"}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>}
|
||||||
|
entryFeeder={entryFeeder}
|
||||||
|
np={block.selection}
|
||||||
|
counterPart={vps.verb ? object : undefined}
|
||||||
|
role={(isPast && vps.verb.transitivity !== "intransitive")
|
||||||
|
? "ergative"
|
||||||
|
: "subject"
|
||||||
|
}
|
||||||
|
onChange={handleSubjectChange}
|
||||||
|
opts={opts}
|
||||||
|
isShrunk={(servantIsShrunk && roles.servant === "subject")}
|
||||||
|
/>
|
||||||
|
: (vps.verb && block?.type === "objectSelection" && block.selection !== "none")
|
||||||
|
? <div className="my-2" style={{ background: (servantIsShrunk && roles.servant === "object") ? shrunkenBackground : "inherit" }}>
|
||||||
|
{(typeof block.selection === "number")
|
||||||
|
? <div>
|
||||||
|
{roles.king === "object"
|
||||||
|
? <div className="h5 text-center clickable" onClick={() => setShowingExplanation({ role: "king", item: "object" })}>Object {roleIcon.king}</div>
|
||||||
|
: <div className="h5 text-center">Object</div>}
|
||||||
|
<div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
|
||||||
|
</div>
|
||||||
|
: <NPPicker
|
||||||
|
phraseIsComplete={phraseIsComplete}
|
||||||
|
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">
|
||||||
|
Object
|
||||||
|
{` `}
|
||||||
|
<span className="clickable" onClick={() => setShowingExplanation({ role: "servant", item: "object" })}>{roleIcon.servant}</span>
|
||||||
|
{` `}
|
||||||
|
{(rendered && rendered.whatsAdjustable !== "king") &&
|
||||||
|
<span onClick={() => adjustVps({ type: "toggle servant shrink" })} className="mx-2 clickable">
|
||||||
|
{!servantIsShrunk ? "🪄" : "👶"}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>}
|
||||||
|
entryFeeder={entryFeeder}
|
||||||
|
role="object"
|
||||||
|
np={block.selection}
|
||||||
|
counterPart={subject}
|
||||||
|
onChange={handleObjectChange}
|
||||||
|
opts={opts}
|
||||||
|
isShrunk={(servantIsShrunk && roles.servant === "object")}
|
||||||
|
/>}
|
||||||
|
</div>
|
||||||
|
: null}
|
||||||
|
</div>;
|
||||||
|
})}
|
||||||
|
<div className="my-2">
|
||||||
|
<TensePicker
|
||||||
|
vps={vps}
|
||||||
|
onChange={adjustVps}
|
||||||
|
mode="phrases"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<VPExplorerExplanationModal
|
||||||
|
showing={showingExplanation}
|
||||||
|
setShowing={setShowingExplanation}
|
||||||
|
/>
|
||||||
|
{alert && <div className="alert alert-warning text-center" role="alert" style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "30%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
zIndex: 9999999999999,
|
||||||
|
}}>
|
||||||
|
{alert}
|
||||||
|
</div>}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VPPicker;
|
|
@ -44,11 +44,14 @@ export function getVerbFromBlocks(blocks: T.Block[][]): T.VerbRenderedBlock {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRenderedBlock, perfectiveHead: T.PerfectiveHeadBlock } {
|
export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRenderedBlock, perfectiveHead: T.PerfectiveHeadBlock | undefined } {
|
||||||
const verb = getVerbFromBlocks(blocks);
|
const verb = getVerbFromBlocks(blocks);
|
||||||
const perfectiveHead = blocks[0].find(f => f.type === "perfectiveHead");
|
const perfectiveHead = blocks[0].find(f => f.type === "perfectiveHead");
|
||||||
if (!perfectiveHead || perfectiveHead.type !== "perfectiveHead") {
|
if (!perfectiveHead || perfectiveHead.type !== "perfectiveHead") {
|
||||||
throw new Error("perfectiveHead not found in blocks");
|
return {
|
||||||
|
verb,
|
||||||
|
perfectiveHead: undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
verb,
|
verb,
|
||||||
|
@ -56,6 +59,13 @@ export function getVerbAndHeadFromBlocks(blocks: T.Block[][]): { verb: T.VerbRen
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function includesShrunkenServant(kids?: T.Kid[]): boolean {
|
||||||
|
if (!kids) return false;
|
||||||
|
return kids.some(k => (
|
||||||
|
k.type === "mini-pronoun" && k.source === "servant"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.PredicateSelectionComplete> {
|
export function getPredicateSelectionFromBlocks(blocks: T.Block[][]): T.Rendered<T.PredicateSelectionComplete> {
|
||||||
const b = blocks[0].find(f => f.type === "predicateSelection");
|
const b = blocks[0].find(f => f.type === "predicateSelection");
|
||||||
if (!b || b.type !== "predicateSelection") {
|
if (!b || b.type !== "predicateSelection") {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import VerbInfo, { RootsAndStems } from "./components/verb-info/VerbInfo";
|
||||||
import VPExplorer from "./components/vp-explorer/VPExplorer";
|
import VPExplorer from "./components/vp-explorer/VPExplorer";
|
||||||
import useStickyState from "./lib/useStickyState";
|
import useStickyState from "./lib/useStickyState";
|
||||||
import Block, { NPBlock, APBlock } from "./components/blocks/Block";
|
import Block, { NPBlock, APBlock } from "./components/blocks/Block";
|
||||||
|
import { roleIcon } from "./components/vp-explorer/VPExplorerExplanationModal";
|
||||||
import {
|
import {
|
||||||
makePsString,
|
makePsString,
|
||||||
removeFVarients,
|
removeFVarients,
|
||||||
|
@ -158,6 +159,8 @@ import genderColors from "./lib/gender-colors";
|
||||||
import * as Types from "./types";
|
import * as Types from "./types";
|
||||||
import * as typePredicates from "./lib/type-predicates";
|
import * as typePredicates from "./lib/type-predicates";
|
||||||
import APPicker from "./components/ap-picker/APPicker";
|
import APPicker from "./components/ap-picker/APPicker";
|
||||||
|
import VPDisplay from "./components/vp-explorer/VPDisplay";
|
||||||
|
import VPPicker from "./components/vp-explorer/VPPicker";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
|
@ -228,6 +231,7 @@ export {
|
||||||
getPashtoFromRendered,
|
getPashtoFromRendered,
|
||||||
renderAPSelection,
|
renderAPSelection,
|
||||||
getEnglishVerb,
|
getEnglishVerb,
|
||||||
|
roleIcon,
|
||||||
// protobuf helpers
|
// protobuf helpers
|
||||||
readDictionary,
|
readDictionary,
|
||||||
writeDictionary,
|
writeDictionary,
|
||||||
|
@ -254,7 +258,9 @@ export {
|
||||||
APBlock,
|
APBlock,
|
||||||
Block,
|
Block,
|
||||||
EPDisplay,
|
EPDisplay,
|
||||||
|
VPDisplay,
|
||||||
EPPicker,
|
EPPicker,
|
||||||
|
VPPicker,
|
||||||
// OTHER
|
// OTHER
|
||||||
typePredicates,
|
typePredicates,
|
||||||
grammarUnits,
|
grammarUnits,
|
||||||
|
|
Loading…
Reference in New Issue