Editor now has buttons to easily add custom content

This commit is contained in:
DrMint 2022-04-04 11:08:41 +02:00
parent 3f452c529a
commit 286187135f
2 changed files with 319 additions and 48 deletions

View File

@ -128,7 +128,7 @@ export default function Markdawn(props: Props): JSX.Element {
),
},
Sep: {
component: () => <div className="my-24"></div>,
component: () => <div className="my-18"></div>,
},
SceneBreak: {
component: (compProps: { id: string }) => (

View File

@ -1,80 +1,351 @@
import AppLayout from "components/AppLayout";
import Button from "components/Button";
import Markdawn from "components/Markdown/Markdawn";
import ContentPanel, {
ContentPanelWidthSizes,
} from "components/Panels/ContentPanel";
import Popup from "components/Popup";
import ToolTip from "components/ToolTip";
import { GetStaticPropsContext } from "next";
import Script from "next/script";
import { AppStaticProps, getAppStaticProps } from "queries/getAppStaticProps";
import { useCallback, useState } from "react";
import { default as TurndownService } from "turndown";
import TurndownService from "turndown";
interface Props extends AppStaticProps {}
export default function Editor(props: Props): JSX.Element {
const handleInput = useCallback((event) => {
setMarkdown(event.target.value);
const handleInput = useCallback((text: string) => {
setMarkdown(text);
}, []);
const [markdown, setMarkdown] = useState("");
const [converterOpened, setConverterOpened] = useState(false);
function insert(
text: string,
prepend: string,
append: string,
selectionStart: number,
selectionEnd: number
): string {
let newText = text.slice(0, selectionStart);
newText += prepend;
newText += text.slice(selectionStart, selectionEnd);
newText += append;
newText += text.slice(selectionEnd);
return newText;
}
const contentPanel = (
<ContentPanel width={ContentPanelWidthSizes.large}>
<Script
id="autoFitTextArea"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
const el = document.querySelector("#editorTextArea")
el.addEventListener('input', function() {
this.style.height = (this.scrollHeight) + 'px';
});
`,
}}
/>
<Popup setState={setConverterOpened} state={converterOpened}>
<div className="text-center">
<h2 className="mt-4">Convert HTML to markdown</h2>
<p>
Copy and paste any HTML content (content from web pages) here.{" "}
<br />
The text will immediatly be converted to valid Markdown.
<br />
You can then copy the converted text and paste it anywhere you want
in the editor
</p>
</div>
<textarea
readOnly
id="htmlMdTextArea"
title="Ouput textarea"
onPaste={(event) => {
const turndownService = new TurndownService({
headingStyle: "atx",
codeBlockStyle: "fenced",
bulletListMarker: "-",
emDelimiter: "_",
strongDelimiter: "**",
});
let paste = event.clipboardData.getData("text/html");
paste = paste.replace(/<!--.*?-->/u, "");
paste = turndownService.turndown(paste);
paste = paste.replace(/<!--.*?-->/u, "");
const target = event.target as HTMLTextAreaElement;
target.value = paste;
target.select();
event.preventDefault();
}}
className="font-monospace w-[50vw] h-[50vh] mobile:w-[75vw]"
/>
</Popup>
<div className="flex flex-row gap-2 mb-4">
<ToolTip
placement="bottom"
content={
<>
<h3 className="text-lg">Transcript container</h3>
<p>
Use this to create dialogues and transcripts. You can then add
transcript speech line within (
<span className="material-icons text-xs">
record_voice_over
</span>
)
</p>
</>
}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"\n<Transcript>\n",
"\n</Transcript>\n",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">question_answer</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={
<>
<h3 className="text-lg">Transcript speech line</h3>
<p>
Use to add a dialogue/transcript line. Change the{" "}
<kbd>name</kbd> property to chang the name of the speaker
</p>
</>
}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"<Line name=\"speaker\">",
"</Line>\n",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">record_voice_over</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Vertical spacer</h3>}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"<Sep />",
"",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">density_large</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Inset box</h3>}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"\n<InsetBox>\n",
"\n</InsetBox>\n",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">check_box_outline_blank</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Scene break</h3>}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"\n\n<SceneBreak />\n\n",
"",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">more_horiz</span>
</Button>
</ToolTip>
<ToolTip
content={
<div className="flex flex-col place-items-center gap-2">
<h3 className="text-lg">Intralink</h3>
<ToolTip
placement="right"
content={
<>
<h3 className="text-lg">Intralink</h3>
<p className="text-xs">
Interlinks are used to add links to a header within the
same document
</p>
</>
}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"<IntraLink>",
"</IntraLink>",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">link</span>
</Button>
</ToolTip>
<ToolTip
placement="right"
content={
<>
<h3 className="text-lg">Intralink (with target)</h3>{" "}
<p className="text-xs">
Use this one if you want the intralink text to be
different from the target header&rsquo;s name.
</p>
</>
}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
'<IntraLink target="target">',
"</IntraLink>",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<p className="flex place-items-center gap-1">
<span className="material-icons">link</span>+ target
</p>
</Button>
</ToolTip>
</div>
}
>
<Button>
<span className="material-icons">link</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Player&rsquo;s name placeholder</h3>}
>
<Button
onClick={() => {
const textarea = document.querySelector(
"#editorTextArea"
) as HTMLTextAreaElement;
const { value, selectionStart, selectionEnd } = textarea;
textarea.value = insert(
value,
"<player>",
"",
selectionStart,
selectionEnd
);
handleInput(textarea.value);
}}
>
<span className="material-icons">person</span>
</Button>
</ToolTip>
<ToolTip
placement="bottom"
content={<h3 className="text-lg">Open HTML Converter</h3>}
>
<Button
onClick={() => {
setConverterOpened(true);
}}
>
<span className="material-icons">html</span>
</Button>
</ToolTip>
</div>
<div className="grid grid-cols-2 gap-8">
<div>
<h2>Editor</h2>
<textarea
id="editorTextArea"
onInput={handleInput}
className="bg-mid rounded-xl p-8 w-full font-monospace"
onInput={(event) => {
const textarea = event.target as HTMLTextAreaElement;
handleInput(textarea.value);
}}
className="bg-mid !bg-opacity-40 rounded-xl outline-none p-8 w-full text-black font-monospace h-[70vh]"
value={markdown}
title="Input textarea"
/>
<h2 className="mt-4">Convert text to markdown</h2>
<textarea
readOnly
id="htmlMdTextArea"
title="Ouput textarea"
onPaste={(event) => {
const turndownService = new TurndownService({
headingStyle: "atx",
codeBlockStyle: "fenced",
bulletListMarker: "-",
emDelimiter: "*",
strongDelimiter: "**",
});
let paste = event.clipboardData.getData("text/html");
paste = paste.replace(/<!--.*?-->/u, "");
paste = turndownService.turndown(paste);
paste = paste.replace(/<!--.*?-->/u, "");
const target = event.target as HTMLTextAreaElement;
target.value = paste;
target.select();
event.preventDefault();
}}
className="font-monospace"
/>
</div>
<div>
<h2>Preview</h2>
<div className="bg-mid rounded-xl p-8">
<Markdawn className="max-w-full" text={markdown} />
<div className="bg-mid bg-opacity-40 rounded-xl p-8 h-[70vh] overflow-scroll">
<Markdawn className="w-full" text={markdown} />
</div>
</div>
</div>