ability to share phrase url
This commit is contained in:
parent
24e85f9df6
commit
b75ddb9689
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@lingdocs/pashto-inflector",
|
||||
"version": "2.3.7",
|
||||
"version": "2.3.8",
|
||||
"author": "lingdocs.com",
|
||||
"description": "A Pashto inflection and verb conjugation engine, inculding React components for displaying Pashto text, inflections, and conjugations",
|
||||
"homepage": "https://verbs.lingdocs.com",
|
||||
|
@ -24,6 +24,8 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"jsurl2": "^2.1.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"pbf": "^3.2.1",
|
||||
"rambda": "^6.7.0",
|
||||
"react-select": "^5.2.2"
|
||||
|
|
|
@ -2,11 +2,13 @@ import * as T from "../../types";
|
|||
import { renderEP } from "../../lib/phrase-building/render-ep";
|
||||
import { compileEP } from "../../lib/phrase-building/compile-ep";
|
||||
import AbbreviationFormSelector from "../vp-explorer/AbbreviationFormSelector";
|
||||
import useStickyState from "../../lib/useStickyState";
|
||||
import Examples from "../Examples";
|
||||
|
||||
function EPDisplay({ eps, opts }: { eps: T.EPSelectionState, opts: T.TextOptions }) {
|
||||
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationFormEq");
|
||||
function EPDisplay({ eps, opts, setForm }: {
|
||||
eps: T.EPSelectionState,
|
||||
opts: T.TextOptions,
|
||||
setForm: (form: T.FormVersion) => void,
|
||||
}) {
|
||||
const EP = completeEPSelection(eps);
|
||||
if (!EP) {
|
||||
return <div className="lead text-center my-4">
|
||||
|
@ -20,11 +22,11 @@ function EPDisplay({ eps, opts }: { eps: T.EPSelectionState, opts: T.TextOptions
|
|||
</div>
|
||||
}
|
||||
const rendered = renderEP(EP);
|
||||
const result = compileEP(rendered, form);
|
||||
const result = compileEP(rendered, EP.form);
|
||||
return <div className="text-center pt-3">
|
||||
<AbbreviationFormSelector
|
||||
adjustable="king"
|
||||
form={{ ...form, shrinkServant: false }}
|
||||
form={EP.form}
|
||||
onChange={setForm}
|
||||
/>
|
||||
{"long" in result.ps ?
|
||||
|
|
|
@ -32,7 +32,8 @@ function EPExplorer(props: {
|
|||
negative: false,
|
||||
},
|
||||
shrunkenPossesive: undefined,
|
||||
}, "EPSelectionState3");
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
}, "EPSelectionState10");
|
||||
function handlePredicateTypeChange(type: "NP" | "Complement") {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
|
@ -63,6 +64,12 @@ function EPExplorer(props: {
|
|||
shrunkenPossesive,
|
||||
}));
|
||||
}
|
||||
function handleSetForm(form: T.FormVersion) {
|
||||
setEps(o => ({
|
||||
...o,
|
||||
form,
|
||||
}));
|
||||
}
|
||||
const king = eps.subject?.type === "pronoun"
|
||||
? "subject"
|
||||
: eps.predicate.type === "Complement"
|
||||
|
@ -134,7 +141,11 @@ function EPExplorer(props: {
|
|||
</div>
|
||||
</div>
|
||||
{mode === "charts" && <EqChartsDisplay tense={eps.equative.tense} opts={props.opts} />}
|
||||
{mode === "phrases" && <EPDisplay opts={props.opts} eps={eps} />}
|
||||
{mode === "phrases" && <EPDisplay
|
||||
opts={props.opts}
|
||||
eps={eps}
|
||||
setForm={handleSetForm}
|
||||
/>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ function NPPicker(props: {
|
|||
const [npType, setNpType] = useState<T.NPType | undefined>(props.np ? props.np.type : undefined);
|
||||
useEffect(() => {
|
||||
setNpType(props.np ? props.np.type : undefined);
|
||||
// setAddingPoss(false);
|
||||
}, [props.np]);
|
||||
function handleClear() {
|
||||
if (props.np && props.np.type === "noun" && props.np.dynamicComplement) return;
|
||||
|
|
|
@ -8,8 +8,11 @@ import {
|
|||
import useStickyState from "../../lib/useStickyState";
|
||||
import Examples from "../Examples";
|
||||
|
||||
function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }) {
|
||||
const [form, setForm] = useStickyState<T.FormVersion>({ removeKing: false, shrinkServant: false }, "abbreviationForm");
|
||||
function VPDisplay({ VP, opts, setForm }: {
|
||||
VP: T.VPSelectionState,
|
||||
opts: T.TextOptions,
|
||||
setForm: (form: T.FormVersion) => void,
|
||||
}) {
|
||||
const [OSV, setOSV] = useStickyState<boolean>(false, "includeOSV");
|
||||
const VPComplete = completeVPSelection(VP);
|
||||
if (!VPComplete) {
|
||||
|
@ -20,7 +23,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }
|
|||
})()}
|
||||
</div>;
|
||||
}
|
||||
const result = compileVP(renderVP(VPComplete), { ...form, OSV });
|
||||
const result = compileVP(renderVP(VPComplete), { ...VP.form, OSV });
|
||||
return <div className="text-center mt-1">
|
||||
{VP.verb.transitivity === "transitive" && <div className="form-check mb-2">
|
||||
<input
|
||||
|
@ -36,7 +39,7 @@ function VPDisplay({ VP, opts }: { VP: T.VPSelectionState, opts: T.TextOptions }
|
|||
</div>}
|
||||
<AbbreviationFormSelector
|
||||
adjustable={whatsAdjustable(VPComplete)}
|
||||
form={form}
|
||||
form={VP.form}
|
||||
onChange={setForm}
|
||||
/>
|
||||
{"long" in result.ps ?
|
||||
|
|
|
@ -16,17 +16,11 @@ import { isPastTense } from "../../lib/phrase-building/vp-tools";
|
|||
import VPExplorerQuiz from "./VPExplorerQuiz";
|
||||
import { switchSubjObj } from "../../lib/phrase-building/vp-tools";
|
||||
import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationModal";
|
||||
// @ts-ignore
|
||||
import LZString from "lz-string";
|
||||
|
||||
// TO FINISH IMPERATIVE STUFF!!
|
||||
// TODO: English Builders for imperatives
|
||||
// TODO: Quiz with imperatives
|
||||
const phraseURLParam = "VPPhrase";
|
||||
|
||||
// TODO: make answerFeedback emojis appear at random translate angles a little bit
|
||||
// add energy drinks?
|
||||
|
||||
// TODO: Drill Down text display options
|
||||
|
||||
// TODO: SHOW KING AND SERVANT ONCE TENSE PICKED, EVEN IF NPs not selected
|
||||
// TODO: Issue with dynamic compounds english making with plurals
|
||||
// TODO: Issue with "the money were taken"
|
||||
// TODO: Use the same component for PronounPicker and NPPronounPicker (sizing issue)
|
||||
|
@ -38,14 +32,17 @@ import VPExplorerExplanationModal, { roleIcon } from "./VPExplorerExplanationMod
|
|||
// TODO: error handling on error with rendering etc
|
||||
|
||||
export function VPExplorer(props: {
|
||||
loaded?: T.VPSelectionState,
|
||||
verb: T.VerbEntry,
|
||||
opts: T.TextOptions,
|
||||
handleLinkClick: ((ts: number) => void) | "none",
|
||||
entryFeeder: T.EntryFeeder,
|
||||
}) {
|
||||
const [vps, setVps] = useStickyState<T.VPSelectionState>(
|
||||
savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState7",
|
||||
props.loaded
|
||||
? props.loaded
|
||||
: savedVps => makeVPSelectionState(props.verb, savedVps),
|
||||
"vpsState8",
|
||||
);
|
||||
const [mode, setMode] = useStickyState<"charts" | "phrases" | "quiz">(
|
||||
savedMode => {
|
||||
|
@ -55,12 +52,20 @@ export function VPExplorer(props: {
|
|||
},
|
||||
"verbExplorerMode2",
|
||||
);
|
||||
const [showShareClipped, setShowShareClipped] = useState<boolean>(false);
|
||||
const [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
|
||||
const isPast = isPastTense(vps.verb.tenseCategory === "perfect" ? vps.verb.perfectTense : vps.verb.verbTense);
|
||||
const roles = getKingAndServant(
|
||||
isPast,
|
||||
vps.verb.transitivity !== "intransitive",
|
||||
);
|
||||
useEffect(() => {
|
||||
const VPSFromUrl = getVPSFromUrl();
|
||||
if (VPSFromUrl) {
|
||||
setVps(VPSFromUrl);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
setVps(oldVps => {
|
||||
if (mode === "quiz") {
|
||||
|
@ -112,6 +117,21 @@ export function VPExplorer(props: {
|
|||
}
|
||||
return f;
|
||||
}
|
||||
function handleSetForm(form: T.FormVersion) {
|
||||
setVps(o => ({
|
||||
...o,
|
||||
form,
|
||||
}));
|
||||
}
|
||||
// for some crazy reason I can't get the URI share thing to encode and decode properly
|
||||
function handleCopyShareLink() {
|
||||
const shareUrl = getShareUrl(vps);
|
||||
navigator.clipboard.writeText(shareUrl);
|
||||
setShowShareClipped(true);
|
||||
setTimeout(() => {
|
||||
setShowShareClipped(false);
|
||||
}, 1000);
|
||||
}
|
||||
return <div className="mt-3" style={{ maxWidth: "950px"}}>
|
||||
<VerbPicker
|
||||
vps={vps}
|
||||
|
@ -119,7 +139,9 @@ export function VPExplorer(props: {
|
|||
opts={props.opts}
|
||||
handleLinkClick={props.handleLinkClick}
|
||||
/>
|
||||
<div className="mt-2 mb-3 text-center">
|
||||
<div className="mt-2 mb-3 d-flex flex-row justify-content-between align-items-center">
|
||||
<div style={{ width: "1rem" }}>
|
||||
</div>
|
||||
<ButtonSelect
|
||||
value={mode}
|
||||
options={[
|
||||
|
@ -129,6 +151,13 @@ export function VPExplorer(props: {
|
|||
]}
|
||||
handleChange={setMode}
|
||||
/>
|
||||
<div
|
||||
className="clickable"
|
||||
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
|
||||
style={{ width: "1rem" }}
|
||||
>
|
||||
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||
</div>
|
||||
</div>
|
||||
{(vps.verb && (typeof vps.verb.object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
|
||||
<div className="text-center my-2">
|
||||
|
@ -183,13 +212,26 @@ export function VPExplorer(props: {
|
|||
/>
|
||||
</div>
|
||||
</div>}
|
||||
{mode === "phrases" && <VPDisplay VP={vps} opts={props.opts} />}
|
||||
{mode === "phrases" && <VPDisplay
|
||||
VP={vps}
|
||||
opts={props.opts}
|
||||
setForm={handleSetForm}
|
||||
/>}
|
||||
{mode === "charts" && <ChartDisplay VS={vps.verb} opts={props.opts} />}
|
||||
{mode === "quiz" && <VPExplorerQuiz opts={props.opts} vps={vps} />}
|
||||
<VPExplorerExplanationModal
|
||||
showing={showingExplanation}
|
||||
setShowing={setShowingExplanation}
|
||||
/>
|
||||
{showShareClipped && <div className="alert alert-primary text-center" role="alert" style={{
|
||||
position: "fixed",
|
||||
top: "30%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
zIndex: 9999999999999,
|
||||
}}>
|
||||
Phrase URL copied to clipboard
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -202,4 +244,19 @@ function hasPronounConflict(subject: T.NPSelection | undefined, object: undefine
|
|||
return isInvalidSubjObjCombo(subjPronoun.person, objPronoun.person);
|
||||
}
|
||||
|
||||
function getShareUrl(vps: T.VPSelectionState): string {
|
||||
const stringJSON = JSON.stringify(vps);
|
||||
const encoded = LZString.compressToEncodedURIComponent(stringJSON);
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.append(phraseURLParam, encoded);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function getVPSFromUrl(): T.VPSelectionState | undefined {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const fromParams = params.get(phraseURLParam);
|
||||
if (!fromParams) return;
|
||||
const decoded = LZString.decompressFromEncodedURIComponent(fromParams);
|
||||
return JSON.parse(decoded) as T.VPSelectionState;
|
||||
}
|
||||
|
||||
|
|
|
@ -446,6 +446,7 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
subject: subject !== undefined ? subject : randSubj,
|
||||
verb: randomizeTense(verb, true),
|
||||
shrunkenPossesive: undefined,
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
}
|
||||
}
|
||||
const v: T.VerbSelectionComplete = {
|
||||
|
@ -462,6 +463,7 @@ function getRandomVPSelection(mix: MixType = "both") {
|
|||
subject: randSubj,
|
||||
verb: randomizeTense(v, true),
|
||||
shrunkenPossesive: undefined,
|
||||
form: { removeKing: false, shrinkServant: false },
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -66,6 +66,7 @@ export function makeVPSelectionState(
|
|||
canChangeVoice: transitivity === "transitive",
|
||||
canChangeStatDyn: "stative" in info,
|
||||
},
|
||||
form: os ? os.form : { removeKing: false, shrinkServant: false },
|
||||
shrunkenPossesive: os ? os.shrunkenPossesive : undefined,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export function compileEP(EP: T.EPRendered, form: T.FormVersion, combineLengths?
|
|||
};
|
||||
}
|
||||
|
||||
function getSegmentsAndKids(EP: T.EPRendered, form: T.FormVersion): { kids: Segment[], NPs: Segment[] } {
|
||||
function getSegmentsAndKids(EP: T.EPRendered, form: Omit<T.FormVersion, "shrinkServant">): { kids: Segment[], NPs: Segment[] } {
|
||||
function ifNotRemoved(s: Segment, role: "subject" | "predicate"): Segment[] {
|
||||
if (form.removeKing && EP.king === role) {
|
||||
return [];
|
||||
|
|
|
@ -33,6 +33,7 @@ export function renderEP(EP: T.EPSelectionComplete): T.EPRendered {
|
|||
equative: renderEquative(EP.equative, kingPerson),
|
||||
englishBase: equativeBuilders[EP.equative.tense](kingPerson, EP.equative.negative),
|
||||
shrunkenPossesive: EP.shrunkenPossesive,
|
||||
form: EP.form,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ export function renderVP(VP: T.VPSelectionComplete): T.VPRendered {
|
|||
object: VP.verb.isCompound === "dynamic" ? "none" : VP.verb.object,
|
||||
vs: VP.verb,
|
||||
}),
|
||||
form: VP.form,
|
||||
};
|
||||
return b;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -509,6 +509,7 @@ export type VPRendered = {
|
|||
object: Rendered<NPSelection> | ObjectNP,
|
||||
verb: VerbRendered,
|
||||
englishBase?: string[],
|
||||
form: FormVersion,
|
||||
}
|
||||
|
||||
export type VerbTense = "presentVerb"
|
||||
|
@ -531,12 +532,14 @@ export type VPSelectionState = {
|
|||
subject: NPSelection | undefined,
|
||||
verb: VerbSelection,
|
||||
shrunkenPossesive: undefined | number,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type VPSelectionComplete = {
|
||||
subject: NPSelection,
|
||||
verb: VerbSelectionComplete,
|
||||
shrunkenPossesive: undefined | number,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type VerbSelectionComplete = Omit<VerbSelection, "object" | "verbTense" | "perfectTense" | "imperfectiveTense" | "tenseCategory"> & {
|
||||
|
@ -664,6 +667,7 @@ export type EPSelectionState = {
|
|||
},
|
||||
equative: EquativeSelection,
|
||||
shrunkenPossesive: undefined | number,
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"> & {
|
||||
|
@ -675,6 +679,7 @@ export type EPSelectionComplete = Omit<EPSelectionState, "subject" | "predicate"
|
|||
type: "Complement",
|
||||
selection: EqCompSelection,
|
||||
},
|
||||
form: FormVersion,
|
||||
};
|
||||
|
||||
export type EqCompType = "adjective" | "loc. adv."; // TODO: - more
|
||||
|
@ -699,6 +704,7 @@ export type EPRendered = {
|
|||
equative: EquativeRendered,
|
||||
englishBase?: string[],
|
||||
shrunkenPossesive: undefined | number,
|
||||
form: FormVersion,
|
||||
}
|
||||
|
||||
export type EntryFeeder = {
|
||||
|
|
|
@ -7131,6 +7131,11 @@ jsonfile@^6.0.1:
|
|||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsurl2@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsurl2/-/jsurl2-2.1.0.tgz#4c7f0cd33344f318cea5e321089c983194e947c7"
|
||||
integrity sha512-dN5xpdjYWxhkn/hN9jGovVP/sy8HqP3BiNU7nKm7pwtRSfQlKG8FZO7wlO4kmydAYzv3UwVVzGjAnEBr4KCmmw==
|
||||
|
||||
"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b"
|
||||
|
|
Loading…
Reference in New Issue