Skip to content

Commit

Permalink
[#52064] Extract text, setText, sync, setSync into a hook
Browse files Browse the repository at this point in the history
  • Loading branch information
MaciejWas committed Feb 22, 2024
1 parent 648d031 commit edd4caf
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 59 deletions.
47 changes: 8 additions & 39 deletions src/MystEditor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { render } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import { html } from 'htm/preact';
import markdownitDocutils from 'markdown-it-docutils'
import purify from 'dompurify'
import markdownIt from 'markdown-it'
import { StyleSheetManager, styled } from 'styled-components';

import ButtonGroup from "./components/ButtonGroup";
Expand All @@ -12,7 +9,8 @@ import TemplateManager from './components/TemplateManager';
import { TopbarButton } from './components/Buttons';
import Preview from './components/Preview';
import Diff from './components/Diff';
import { markdownReplacer, resetCache, useCustomRoles } from './hooks/markdownReplacer';
import { resetCache } from './hooks/markdownReplacer';
import { useText } from './hooks/useText';

if (!window.myst_editor?.isFresh) {
resetCache();
Expand Down Expand Up @@ -112,17 +110,6 @@ const createExtraScopePlugin = (scope) => {

const hideBodyScrollIf = val => document.documentElement.style.overflow = val ? "hidden" : "visible";

function copyHtmlAsRichText(str) {
function listener(e) {
e.clipboardData.setData("text/html", str);
e.clipboardData.setData("text/plain", str);
e.preventDefault();
}
document.addEventListener("copy", listener);
document.execCommand("copy");
document.removeEventListener("copy", listener);
};

const MystEditor = ({
name = "myst_editor_textarea",
id = "myst_editor_textarea",
Expand All @@ -138,19 +125,8 @@ const MystEditor = ({
}) => {
const [mode, setMode] = useState(initialMode);
const [fullscreen, setFullscreen] = useState(false);
const [text, setText] = useState(initialText);
const text = useText(initialText, transforms, customRoles);
const [alert, setAlert] = useState(null);
const [syncText, setSyncText] = useState(false);

const renderAndSanitize = (text) => {
return purify.sanitize(
markdownIt({ breaks: true, linkify: true })
.use(markdownitDocutils)
.use(markdownReplacer(transforms))
.use(useCustomRoles(customRoles))
.render(text)
)
}

const alertFor = (alertText, secs) => {
setAlert(alertText);
Expand All @@ -161,8 +137,8 @@ const MystEditor = ({
}

const copyHtml = () => {
copyHtmlAsRichText(renderAndSanitize(text))
alertFor("copied!", 2)
text.copy();
alertFor("copied!", 2);
}

const buttonActions = {
Expand All @@ -176,13 +152,6 @@ const MystEditor = ({

useEffect(() => hideBodyScrollIf(fullscreen), [fullscreen])

useEffect(() => {
if (!window.myst_editor) {
window.myst_editor = {};
}
window.myst_editor.text = text;
}, [text])

return html`
<div id="myst-css-namespace">
<${StyleSheetManager} stylisPlugins=${[createExtraScopePlugin('#myst-css-namespace')]}>
Expand All @@ -194,14 +163,14 @@ const MystEditor = ({
<//>
<${TopbarRight}>
<${TopbarButton} type="button" onClick=${(event) => printCallback(event)}>Export as PDF<//>
<${TemplateManager} templatelist=${templatelist} setText=${setText} setSyncText=${setSyncText}/>
<${TemplateManager} text=${text} templatelist=${templatelist} />
<${Separator} />
<${ButtonGroup} buttons=${buttonsRight} clickedId=${2} clickCallback=${(newMode) => setMode(newMode)}/>
<//>
<//>
<${MystWrapper} fullscreen=${fullscreen}>
<${CodeMirror} mode=${mode} text=${text} setText=${setText} syncText=${syncText} setSyncText=${setSyncText} name=${name} id=${id} collaboration=${collaboration} spellcheckOpts=${spellcheckOpts} highlights=${transforms}/>
<${Preview} $mode=${mode} dangerouslySetInnerHTML=${{ __html: renderAndSanitize(text) }}/>
<${CodeMirror} mode=${mode} text=${text} name=${name} id=${id} collaboration=${collaboration} spellcheckOpts=${spellcheckOpts} highlights=${transforms}/>
<${Preview} $mode=${mode} dangerouslySetInnerHTML=${{ __html: text.renderAndSanitize() }}/>
${mode === 'Diff' ? html`<${Diff} oldText=${initialText} text=${text}/>` : "" }
<//>
<//>
Expand Down
26 changes: 10 additions & 16 deletions src/components/CodeMirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,21 +124,21 @@ const setEditorText = (editor, text) => {
});
}

const CodeMirror = ({ text, setText, id, name, className, mode, syncText, setSyncText, collaboration, spellcheckOpts, highlights }) => {
const CodeMirror = ({ text, id, name, className, mode, collaboration, spellcheckOpts, highlights }) => {
const editorRef = useRef(null);
const [initialized, setInitialized] = useState(false);

const { provider, undoManager, ytext, ydoc, ready, ycommentsComponent, ycomments } = useCollaboration(collaboration);

useEffect(() => {
const startState = EditorState.create({
doc: collaboration.enabled ? ytext.toString() : text,
doc: collaboration.enabled ? ytext.toString() : text.get(),
extensions: ExtensionBuilder.basicSetup()
.useHighlighter(highlights)
.useSpellcheck(spellcheckOpts)
.useCollaboration({enabled: collaboration.enabled || false, ytext, undoManager, provider, editorRef})
.useComments({enabled: collaboration.commentsEnabled, ycomments})
.addUpdateListener(update => update.docChanged && setText(view.state.doc.toString()))
.addUpdateListener(update => update.docChanged && text.set(view.state.doc.toString()))
.create()
});

Expand All @@ -150,6 +150,7 @@ const CodeMirror = ({ text, setText, id, name, className, mode, syncText, setSyn
setInitialized(true);

ycomments?.registerCodeMirror(view);


return () => {
if (collaboration.enabled) {
Expand All @@ -168,30 +169,23 @@ const CodeMirror = ({ text, setText, id, name, className, mode, syncText, setSyn
provider.firstUser &&
ready;

if (ytext && ytext.toString().length != 0) setText(ytext.toString());
if (ytext && ytext.toString().length != 0) text.set(ytext.toString());

if (isFirstUser) {
console.log('You are the first user in this document. Initiating...');
setEditorText(editorRef.current, text);
setEditorText(editorRef.current, text.get());
}

ycomments?.updateMainCodeMirror();

}, [ready, initialized]);
text.onSync(currentText => setEditorText(editorRef.current, currentText))

useEffect(() => {
if (syncText) {
console.log('setting text');
setEditorText(editorRef.current, text);
setSyncText(false);
}
}, [syncText]);
ycomments?.updateMainCodeMirror();
}, [ready, initialized]);

return html`
<${CodeEditor} $mode=${mode} id="${id}-editor" class=${className}>
${collaboration.commentsEnabled ? ycommentsComponent() : ""}
<//>
<${HiddenTextArea} value=${text} name=${name} id=${id}><//>
<${HiddenTextArea} value=${text.get()} name=${name} id=${id}><//>
`;
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/Diff.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const Diff = ({ oldText, text }) => {
}
mergeView.current = initMergeView({
old: oldText,
current: text,
current: text.get(),
});

leftRef.current.appendChild(mergeView.current.b.dom)
Expand Down
6 changes: 3 additions & 3 deletions src/components/TemplateManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const validateTemplConfig = templConfig => {
return templConfig;
}

const TemplateManager = ({ setText, templatelist, setSyncText }) => {
const TemplateManager = ({ text, templatelist }) => {
const [template, setTemplate] = useState("");
const [readyTemplates, setReadyTemplates] = useState({});
const [selectedTemplate, setSelectedTemplate] = useState(null);
Expand All @@ -85,8 +85,8 @@ const TemplateManager = ({ setText, templatelist, setSyncText }) => {

const changeDocumentTemplate = (template) => {
setTemplate(readyTemplates[template].templatetext);
setText(readyTemplates[template].templatetext);
setSyncText(true);
text.set(readyTemplates[template].templatetext);
text.sync();
setShowModal(false);
}

Expand Down
55 changes: 55 additions & 0 deletions src/hooks/useText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import markdownitDocutils from 'markdown-it-docutils'
import purify from 'dompurify'
import markdownIt from 'markdown-it'
import { markdownReplacer, useCustomRoles } from './markdownReplacer';
import { useEffect, useMemo, useState } from "preact/hooks";

const exposeText = (text) => () => {
if (!window.myst_editor) {
window.myst_editor = {};
}
window.myst_editor.text = text;
}

const copyHtmlAsRichText = (txt) => {
const listener = (e) => {
e.clipboardData.setData("text/html", txt);
e.clipboardData.setData("text/plain", txt);
e.preventDefault();
}
document.addEventListener("copy", listener);
document.execCommand("copy");
document.removeEventListener("copy", listener);
}

export const useText = (initialText, transforms, customRoles) => {
const [text, setText] = useState(initialText);
const [syncText, setSyncText] = useState(false);
const [onSync, setOnSync] = useState({ action: (text) => { } });

const markdown = useMemo(
() => markdownIt({ breaks: true, linkify: true })
.use(markdownitDocutils)
.use(markdownReplacer(transforms))
.use(useCustomRoles(customRoles)),
[]
);

useEffect(exposeText(text), [text]);

useEffect(() => {
if (syncText) {
onSync.action(text);
setSyncText(false);
}
}, [syncText]);

return {
set: setText,
get() { return text },
sync() { setSyncText(true) },
onSync(action) { setOnSync({ action }) },
renderAndSanitize() { return purify.sanitize(markdown.render(text)) },
copy() { copyHtmlAsRichText(this.renderAndSanitize()) }
}
}

0 comments on commit edd4caf

Please sign in to comment.