Editor now has buttons to easily add custom content
This commit is contained in:
		
							parent
							
								
									3f452c529a
								
							
						
					
					
						commit
						286187135f
					
				@ -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 }) => (
 | 
			
		||||
 | 
			
		||||
@ -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’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’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>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user