add servant and king explanations

This commit is contained in:
lingdocs 2022-04-14 12:12:49 +05:00
parent 9b5e19f39e
commit d9c0ed9630
3 changed files with 109 additions and 28 deletions

View File

@ -1,12 +1,37 @@
import * as T from "../types"; import * as T from "../types";
import Select from "react-select"; import { StyleHTMLAttributes } from "react";
import Select, { StylesConfig } from "react-select";
import AsyncSelect from "react-select/async"; import AsyncSelect from "react-select/async";
import { import {
makeSelectOption, makeSelectOption,
makeVerbSelectOption, makeVerbSelectOption,
zIndexProps,
} from "./np-picker/picker-tools"; } from "./np-picker/picker-tools";
const customStyles: StylesConfig = {
menuPortal: (base) => ({
...base,
zIndex: 99999,
}),
menu: (base) => ({
...base,
zIndex: 999999,
}),
option: (provided, state) => ({
...provided,
padding: "10px 5px",
}),
input: (base) => ({
...base,
padding: 0,
}),
singleValue: (provided, state) => {
const opacity = state.isDisabled ? 0.5 : 1;
const transition = 'opacity 300ms';
return { ...provided, opacity, transition };
}
}
function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
entries: E[] entries: E[]
} | { } | {
@ -18,9 +43,10 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
name: string | undefined, name: string | undefined,
isVerbSelect?: boolean, isVerbSelect?: boolean,
opts: T.TextOptions, opts: T.TextOptions,
style?: StyleHTMLAttributes<HTMLDivElement>,
}) { }) {
const divStyle = props.style || { width: "13rem" };
const placeholder = "entries" in props ? "Select…" : "Search Pashto"; const placeholder = "entries" in props ? "Select…" : "Search Pashto";
const minWidth = "9rem";
function makeOption(e: E | T.DictionaryEntry) { function makeOption(e: E | T.DictionaryEntry) {
if ("entry" in e) { if ("entry" in e) {
return (props.isVerbSelect ? makeVerbSelectOption : makeSelectOption)(e, props.opts); return (props.isVerbSelect ? makeVerbSelectOption : makeSelectOption)(e, props.opts);
@ -42,16 +68,17 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
if (!s) return; if (!s) return;
props.onChange(s); props.onChange(s);
} }
return <div style={{ minWidth }}> return <div style={divStyle}>
<AsyncSelect <AsyncSelect
styles={customStyles}
isSearchable={true} isSearchable={true}
className="mb-2" className="mb-2"
value={value} value={value}
// @ts-ignore
onChange={onChange} onChange={onChange}
defaultOptions={[]} defaultOptions={[]}
loadOptions={options} loadOptions={options}
placeholder={placeholder} placeholder={placeholder}
{...zIndexProps}
/> />
</div>; </div>;
} }
@ -76,15 +103,16 @@ function EntrySelect<E extends T.DictionaryEntry | T.VerbEntry>(props: ({
if (!s) return; if (!s) return;
props.onChange(s); props.onChange(s);
} }
return <div style={{ minWidth }}> return <div style={divStyle}>
<Select <Select
styles={customStyles}
isSearchable={true} isSearchable={true}
value={value} value={value}
// @ts-ignore
onChange={onChange} onChange={onChange}
className="mb-2" className="mb-2"
options={options} options={options}
placeholder={placeholder} placeholder={placeholder}
{...zIndexProps}
/> />
</div> </div>
} }

View File

@ -56,7 +56,7 @@ function NPPicker(props: {
const clearButton = !props.cantClear const clearButton = !props.cantClear
? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button> ? <button className="btn btn-sm btn-light mb-2" onClick={handleClear}>X</button>
: <div></div>; : <div></div>;
return <div> return <div style={{ minWidth: "9rem" }}>
{!npType && <div className="text-center mt-3"> {!npType && <div className="text-center mt-3">
<div className="h6 mr-3"> <div className="h6 mr-3">
Choose NP Choose NP

View File

@ -3,7 +3,7 @@ import VerbPicker from "./VerbPicker";
import TensePicker from "./TensePicker"; import TensePicker from "./TensePicker";
import VPDisplay from "./VPDisplay"; import VPDisplay from "./VPDisplay";
import ButtonSelect from "../ButtonSelect"; import ButtonSelect from "../ButtonSelect";
import { Modal } from "react-bootstrap";
import { import {
isInvalidSubjObjCombo, isInvalidSubjObjCombo,
} from "../../lib/phrase-building/vp-tools"; } from "../../lib/phrase-building/vp-tools";
@ -11,15 +11,14 @@ import * as T from "../../types";
import ChartDisplay from "./ChartDisplay"; import ChartDisplay from "./ChartDisplay";
import useStickyState from "../../lib/useStickyState"; import useStickyState from "../../lib/useStickyState";
import { makeVPSelectionState } from "./verb-selection"; import { makeVPSelectionState } from "./verb-selection";
import { useEffect } from "react"; import { useEffect, useState } from "react";
import { getKingAndServant } from "../../lib/phrase-building/render-vp"; import { getKingAndServant } from "../../lib/phrase-building/render-vp";
import { isPastTense } from "../../lib/phrase-building/vp-tools"; import { isPastTense } from "../../lib/phrase-building/vp-tools";
import VPExplorerQuiz from "./VPExplorerQuiz"; import VPExplorerQuiz from "./VPExplorerQuiz";
import { switchSubjObj } from "../../lib/phrase-building/vp-tools" import { switchSubjObj } from "../../lib/phrase-building/vp-tools";
const kingEmoji = "👑";
const servantEmoji = "🙇‍♂️";
const kingIcon = <i className="mx-1 fas fa-crown" />;
const servantIcon = <i className="mx-1 fas fa-male" />;
// TODO: make answerFeedback emojis appear at random translate angles a little bit // TODO: make answerFeedback emojis appear at random translate angles a little bit
// add energy drinks? // add energy drinks?
@ -61,7 +60,9 @@ export function VPExplorer(props: {
}, },
"verbExplorerMode", "verbExplorerMode",
); );
const [showingKingExplanation, setShowingKingExplanation] = useState<"subject" | "object" | false>(false);
const [showingServantExplanation, setShowingServantExplanation] = useState<"subject" | "object" | false>(false);
const roles = getKingAndServant(isPastTense(vps.verb.tense), vps.verb.transitivity !== "intransitive");
useEffect(() => { useEffect(() => {
setVps(oldVps => { setVps(oldVps => {
if (mode === "quiz") { if (mode === "quiz") {
@ -139,7 +140,9 @@ export function VPExplorer(props: {
{mode !== "quiz" && <div className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}> {mode !== "quiz" && <div className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
{mode === "phrases" && <> {mode === "phrases" && <>
<div className="my-2"> <div className="my-2">
<div className="h5 text-center">Subject {showRole(vps, "subject")}</div> {roles.king === "subject"
? <div className="h5 text-center clickable" onClick={() => setShowingKingExplanation("subject")}>Subject {kingIcon}</div>
: <div className="h5 text-center clickable" onClick={() => setShowingKingExplanation("subject")}>Subject {servantIcon}</div>}
<NPPicker <NPPicker
{..."getNounByTs" in props ? { {..."getNounByTs" in props ? {
getNounByTs: props.getNounByTs, getNounByTs: props.getNounByTs,
@ -157,7 +160,9 @@ export function VPExplorer(props: {
/> />
</div> </div>
{vps.verb && (vps.verb.object !== "none") && <div className="my-2"> {vps.verb && (vps.verb.object !== "none") && <div className="my-2">
<div className="h5 text-center">Object {showRole(vps, "object")}</div> {roles.king === "object"
? <div className="h5 text-center clickable" onClick={() => setShowingKingExplanation("object")}>Object {kingIcon}</div>
: <div className="h5 text-center clickable" onClick={() => setShowingServantExplanation("object")}>Object {servantIcon}</div>}
{(typeof vps.verb.object === "number") {(typeof vps.verb.object === "number")
? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div> ? <div className="text-muted">Unspoken 3rd Pers. Masc. Plur.</div>
: <NPPicker : <NPPicker
@ -189,6 +194,64 @@ export function VPExplorer(props: {
{mode === "phrases" && <VPDisplay VP={vps} opts={props.opts} />} {mode === "phrases" && <VPDisplay VP={vps} opts={props.opts} />}
{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} />}
<Modal show={!!showingKingExplanation} onHide={() => setShowingKingExplanation(false)}>
<Modal.Header closeButton>
<Modal.Title>About the King {kingIcon}</Modal.Title>
</Modal.Header>
<Modal.Body>
In this tense/form, the {showingKingExplanation} is the <strong>king</strong> {kingIcon} of the phrase. That means that:
<ul className="mt-2">
<li>
<div>It controls the verb conjugation. The verb agrees with the gender and number of the king.</div>
</li>
<li>
<div>👍 It <strong>can</strong> be removed / left out from the phrase.</div>
<div className="text-muted">(You can kill the king)</div>
</li>
<li>
<div>🙅 It <strong>cannot be</strong> shrunk it into a <a target="_blank" rel="noreferrer" href="https://grammar.lingdocs.com/pronouns/pronouns-mini/">mini-pronoun</a>. 👶</div>
<div className="text-muted">(You can't shrink the king)</div>
</li>
</ul>
<p>Mnemonic for shortening phrases:</p>
<p className="text-muted">"🚫 Kill the king 👶 Shrink the servant"</p>
</Modal.Body>
<Modal.Footer>
<button type="button" className="btn btn-primary clb" onClick={() => setShowingKingExplanation(false)}>
Close
</button>
</Modal.Footer>
</Modal>
<Modal show={!!showingServantExplanation} onHide={() => setShowingServantExplanation(false)}>
<Modal.Header closeButton>
<Modal.Title>About the Servant {servantIcon}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
In this tense/form, the {showingServantExplanation} is the <strong>servant</strong> {servantIcon} of the phrase. That means that:
</p>
<ul>
<li>
It does not affect the conjugation of the verb. That's the king's job.
</li>
<li>
<div>👍 It <strong>can</strong> shrink it into a <a target="_blank" rel="noreferrer" href="https://grammar.lingdocs.com/pronouns/pronouns-mini/">mini-pronoun</a>. 👶</div>
<div className="text-muted">(You can <strong>shrink the servant</strong>)</div>
</li>
<li>
<div>🙅 It <strong>cannot</strong> be removed / left out of the phrase</div>
<div className="text-muted">(You can't kill the servant)</div>
</li>
</ul>
<p>Mnemonic for shortening phrases:</p>
<p className="text-muted">"🚫 Kill the king 👶 Shrink the servant"</p>
</Modal.Body>
<Modal.Footer>
<button type="button" className="btn btn-primary clb" onClick={() => setShowingServantExplanation(false)}>
Close
</button>
</Modal.Footer>
</Modal>
</div> </div>
} }
@ -201,14 +264,4 @@ function hasPronounConflict(subject: T.NPSelection | undefined, object: undefine
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person); return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
} }
function showRole(VP: T.VPSelection, member: "subject" | "object") {
const roles = getKingAndServant(
isPastTense(VP.verb.tense),
VP.verb.transitivity !== "intransitive",
);
return VP
? <span className="ml-2">
{(roles.king === member ? kingEmoji : roles.servant === member ? servantEmoji : "")}
</span>
: "";
}