ability to copy phrase selection code
This commit is contained in:
parent
dc0cd48015
commit
cab6528a6c
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@lingdocs/pashto-inflector",
|
"name": "@lingdocs/pashto-inflector",
|
||||||
"version": "2.6.8",
|
"version": "2.6.9",
|
||||||
"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",
|
||||||
|
|
|
@ -13,6 +13,9 @@ import { completeEPSelection } from "../../lib/phrase-building/render-ep";
|
||||||
import { makeEPSBlocks, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
import { makeEPSBlocks, getSubjectSelection } from "../../lib/phrase-building/blocks-utils";
|
||||||
import APPicker from "../ap-picker/APPicker";
|
import APPicker from "../ap-picker/APPicker";
|
||||||
import autoAnimate from "@formkit/auto-animate";
|
import autoAnimate from "@formkit/auto-animate";
|
||||||
|
// @ts-ignore
|
||||||
|
import LZString from "lz-string";
|
||||||
|
const phraseURLParam = "EPhrase";
|
||||||
|
|
||||||
const blankEps: T.EPSelectionState = {
|
const blankEps: T.EPSelectionState = {
|
||||||
blocks: makeEPSBlocks(),
|
blocks: makeEPSBlocks(),
|
||||||
|
@ -37,10 +40,22 @@ function EPExplorer(props: {
|
||||||
const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode");
|
const [mode, setMode] = useStickyState<"charts" | "phrases">("charts", "EPExplorerMode");
|
||||||
const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState4", flashMessage);
|
const [eps, adjustEps] = useStickyReducer(epsReducer, blankEps, "EPState4", flashMessage);
|
||||||
const [alert, setAlert] = useState<string | undefined>(undefined);
|
const [alert, setAlert] = useState<string | undefined>(undefined);
|
||||||
|
const [showClipped, setShowClipped] = useState<string>("");
|
||||||
const parent = useRef<HTMLDivElement>(null);
|
const parent = useRef<HTMLDivElement>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
parent.current && autoAnimate(parent.current);
|
parent.current && autoAnimate(parent.current);
|
||||||
}, [parent]);
|
}, [parent]);
|
||||||
|
useEffect(() => {
|
||||||
|
const EPSFromUrl = getEPSFromUrl();
|
||||||
|
if (EPSFromUrl) {
|
||||||
|
setMode("phrases");
|
||||||
|
adjustEps({
|
||||||
|
type: "load EPS",
|
||||||
|
payload: EPSFromUrl,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
const subject = getSubjectSelection(eps.blocks).selection;
|
const subject = getSubjectSelection(eps.blocks).selection;
|
||||||
const king = subject?.type === "pronoun"
|
const king = subject?.type === "pronoun"
|
||||||
? "subject"
|
? "subject"
|
||||||
|
@ -53,9 +68,26 @@ function EPExplorer(props: {
|
||||||
setAlert(undefined);
|
setAlert(undefined);
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
|
function flashClippedMessage(m: string) {
|
||||||
|
setShowClipped(m);
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowClipped("");
|
||||||
|
}, 1250);
|
||||||
|
}
|
||||||
|
function handleCopyCode() {
|
||||||
|
const code = getCode(eps);
|
||||||
|
navigator.clipboard.writeText(code);
|
||||||
|
flashClippedMessage("Copied phrase code to clipboard");
|
||||||
|
}
|
||||||
|
function handleCopyShareLink() {
|
||||||
|
const shareUrl = getShareUrl(eps);
|
||||||
|
navigator.clipboard.writeText(shareUrl);
|
||||||
|
flashClippedMessage("Copied phrase URL to clipboard");
|
||||||
|
}
|
||||||
const phraseIsComplete = !!completeEPSelection(eps);
|
const phraseIsComplete = !!completeEPSelection(eps);
|
||||||
return <div>
|
return <div>
|
||||||
<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 />
|
||||||
<ButtonSelect
|
<ButtonSelect
|
||||||
value={mode}
|
value={mode}
|
||||||
options={[
|
options={[
|
||||||
|
@ -64,6 +96,22 @@ function EPExplorer(props: {
|
||||||
]}
|
]}
|
||||||
handleChange={setMode}
|
handleChange={setMode}
|
||||||
/>
|
/>
|
||||||
|
<div className="d-flex flex-row">
|
||||||
|
<div
|
||||||
|
className="clickable mr-4"
|
||||||
|
onClick={mode === "phrases" ? handleCopyCode : undefined}
|
||||||
|
style={{ width: "1rem" }}
|
||||||
|
>
|
||||||
|
{(mode === "phrases" && phraseIsComplete) ? <i className="fas fa-code" /> : ""}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="clickable"
|
||||||
|
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
|
||||||
|
style={{ width: "1rem" }}
|
||||||
|
>
|
||||||
|
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{mode === "phrases" && <div className="clickable h5" onClick={() => adjustEps({ type: "insert new AP" })}>+ AP</div>}
|
{mode === "phrases" && <div className="clickable h5" onClick={() => adjustEps({ 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" }}>
|
<div ref={parent} className="d-flex flex-row justify-content-around flex-wrap" style={{ marginLeft: "-0.5rem", marginRight: "-0.5rem" }}>
|
||||||
|
@ -160,7 +208,39 @@ function EPExplorer(props: {
|
||||||
}}>
|
}}>
|
||||||
{alert}
|
{alert}
|
||||||
</div>}
|
</div>}
|
||||||
|
{showClipped && <div className="alert alert-primary text-center" role="alert" style={{
|
||||||
|
position: "fixed",
|
||||||
|
top: "30%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
zIndex: 9999999999999,
|
||||||
|
}}>
|
||||||
|
{showClipped}
|
||||||
|
</div>}
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EPExplorer;
|
export default EPExplorer;
|
||||||
|
|
||||||
|
function getShareUrl(eps: T.EPSelectionState): string {
|
||||||
|
const code = getCode(eps);
|
||||||
|
const encoded = LZString.compressToEncodedURIComponent(code);
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
// need to delete or else you could just get a second param written after
|
||||||
|
// which gets ignored
|
||||||
|
url.searchParams.delete(phraseURLParam);
|
||||||
|
url.searchParams.append(phraseURLParam, encoded);
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCode(eps: T.EPSelectionState): string {
|
||||||
|
return JSON.stringify(eps);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEPSFromUrl(): T.EPSelectionState | 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.EPSelectionState;
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,10 @@ type EpsReducerAction = {
|
||||||
index: number,
|
index: number,
|
||||||
direction: "back" | "forward",
|
direction: "back" | "forward",
|
||||||
},
|
},
|
||||||
};
|
} | {
|
||||||
|
type: "load EPS",
|
||||||
|
payload: T.EPSelectionState,
|
||||||
|
}
|
||||||
|
|
||||||
export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAction, sendAlert?: (msg: string) => void): T.EPSelectionState {
|
export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAction, sendAlert?: (msg: string) => void): T.EPSelectionState {
|
||||||
if (action.type === "set predicate type") {
|
if (action.type === "set predicate type") {
|
||||||
|
@ -173,6 +176,9 @@ export default function epsReducer(eps: T.EPSelectionState, action: EpsReducerAc
|
||||||
blocks: shiftBlock(eps.blocks, index, direction),
|
blocks: shiftBlock(eps.blocks, index, direction),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (action.type === "load EPS") {
|
||||||
|
return action.payload;
|
||||||
|
}
|
||||||
throw new Error("unknown epsReducer action");
|
throw new Error("unknown epsReducer action");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import APPicker from "../ap-picker/APPicker";
|
||||||
import autoAnimate from "@formkit/auto-animate";
|
import autoAnimate from "@formkit/auto-animate";
|
||||||
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
import { getObjectSelection, getSubjectSelection, isNoObject } from "../../lib/phrase-building/blocks-utils";
|
||||||
|
|
||||||
const phraseURLParam = "VPPhrase";
|
const phraseURLParam = "VPhrase";
|
||||||
|
|
||||||
// TODO: Issue with dynamic compounds english making with plurals
|
// TODO: Issue with dynamic compounds english making with plurals
|
||||||
// TODO: Issue with "the money were taken"
|
// TODO: Issue with "the money were taken"
|
||||||
|
@ -55,7 +55,7 @@ function VPExplorer(props: {
|
||||||
},
|
},
|
||||||
"verbExplorerMode2",
|
"verbExplorerMode2",
|
||||||
);
|
);
|
||||||
const [showShareClipped, setShowShareClipped] = useState<boolean>(false);
|
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 [showingExplanation, setShowingExplanation] = useState<{ role: "servant" | "king", item: "subject" | "object" } | false>(false);
|
||||||
const parent = useRef<HTMLDivElement>(null);
|
const parent = useRef<HTMLDivElement>(null);
|
||||||
|
@ -121,14 +121,22 @@ function VPExplorer(props: {
|
||||||
payload: form,
|
payload: form,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function flashClippedMessage(m: string) {
|
||||||
|
setShowClipped(m);
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowClipped("");
|
||||||
|
}, 1250);
|
||||||
|
}
|
||||||
// for some crazy reason I can't get the URI share thing to encode and decode properly
|
// for some crazy reason I can't get the URI share thing to encode and decode properly
|
||||||
function handleCopyShareLink() {
|
function handleCopyShareLink() {
|
||||||
const shareUrl = getShareUrl(vps);
|
const shareUrl = getShareUrl(vps);
|
||||||
navigator.clipboard.writeText(shareUrl);
|
navigator.clipboard.writeText(shareUrl);
|
||||||
setShowShareClipped(true);
|
flashClippedMessage("Copied phrase URL to clipboard");
|
||||||
setTimeout(() => {
|
}
|
||||||
setShowShareClipped(false);
|
function handleCopyCode() {
|
||||||
}, 1250);
|
const code = getCode(vps);
|
||||||
|
navigator.clipboard.writeText(code);
|
||||||
|
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 subject = getSubjectSelection(vps.blocks).selection;
|
||||||
|
@ -160,6 +168,14 @@ function VPExplorer(props: {
|
||||||
]}
|
]}
|
||||||
handleChange={setMode}
|
handleChange={setMode}
|
||||||
/>
|
/>
|
||||||
|
<div className="d-flex flex-row">
|
||||||
|
<div
|
||||||
|
className="clickable mr-4"
|
||||||
|
onClick={mode === "phrases" ? handleCopyCode : undefined}
|
||||||
|
style={{ width: "1rem" }}
|
||||||
|
>
|
||||||
|
{(mode === "phrases" && phraseIsComplete) ? <i className="fas fa-code" /> : ""}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="clickable"
|
className="clickable"
|
||||||
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
|
onClick={mode === "phrases" ? handleCopyShareLink : undefined}
|
||||||
|
@ -168,6 +184,7 @@ function VPExplorer(props: {
|
||||||
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
{mode === "phrases" ? <i className="fas fa-share-alt" /> : ""}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{(vps.verb && (typeof object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
|
{(vps.verb && (typeof object === "object") && (vps.verb.isCompound !== "dynamic") && (vps.verb.tenseCategory !== "imperative") &&(mode === "phrases")) &&
|
||||||
<div className="text-center my-2">
|
<div className="text-center my-2">
|
||||||
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
<button onClick={handleSubjObjSwap} className="btn btn-sm btn-light">
|
||||||
|
@ -288,14 +305,14 @@ function VPExplorer(props: {
|
||||||
showing={showingExplanation}
|
showing={showingExplanation}
|
||||||
setShowing={setShowingExplanation}
|
setShowing={setShowingExplanation}
|
||||||
/>
|
/>
|
||||||
{showShareClipped && <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%",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%)",
|
||||||
zIndex: 9999999999999,
|
zIndex: 9999999999999,
|
||||||
}}>
|
}}>
|
||||||
Phrase URL copied to clipboard
|
{showClipped}
|
||||||
</div>}
|
</div>}
|
||||||
{alert && <div className="alert alert-warning text-center" role="alert" style={{
|
{alert && <div className="alert alert-warning text-center" role="alert" style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
|
@ -312,8 +329,8 @@ function VPExplorer(props: {
|
||||||
export default VPExplorer;
|
export default VPExplorer;
|
||||||
|
|
||||||
function getShareUrl(vps: T.VPSelectionState): string {
|
function getShareUrl(vps: T.VPSelectionState): string {
|
||||||
const stringJSON = JSON.stringify(vps);
|
const code = getCode(vps);
|
||||||
const encoded = LZString.compressToEncodedURIComponent(stringJSON);
|
const encoded = LZString.compressToEncodedURIComponent(code);
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
// need to delete or else you could just get a second param written after
|
// need to delete or else you could just get a second param written after
|
||||||
// which gets ignored
|
// which gets ignored
|
||||||
|
@ -322,6 +339,10 @@ function getShareUrl(vps: T.VPSelectionState): string {
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCode(vps: T.VPSelectionState): string {
|
||||||
|
return JSON.stringify(vps);
|
||||||
|
}
|
||||||
|
|
||||||
function getVPSFromUrl(): T.VPSelectionState | undefined {
|
function getVPSFromUrl(): T.VPSelectionState | undefined {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const fromParams = params.get(phraseURLParam);
|
const fromParams = params.get(phraseURLParam);
|
||||||
|
|
Loading…
Reference in New Issue