From 4296f52adea938ae89abdb4c6f9a71d59b013889 Mon Sep 17 00:00:00 2001 From: Zaydek Michels-Gualtieri Date: Sun, 5 Jul 2020 15:35:20 +0900 Subject: [PATCH] Added a handler to disable read-only mode when DOMContentLoaded fires --- src/Editor/Editor.js | 52 +++++---- src/Editor/notes.js | 189 -------------------------------- src/Editor/useEditor/index.js | 10 +- src/Editor/useEditor/methods.js | 19 ++-- 4 files changed, 43 insertions(+), 227 deletions(-) delete mode 100644 src/Editor/notes.js diff --git a/src/Editor/Editor.js b/src/Editor/Editor.js index 38835fb..c303a36 100644 --- a/src/Editor/Editor.js +++ b/src/Editor/Editor.js @@ -28,6 +28,18 @@ const Editor = ({ markup, children }) => { const [state, dispatch] = useEditor({ markup, children }) + // Disables read-only mode on DOMContentLoaded. + React.useEffect(() => { + const handler = e => { + dispatch.disableReadOnlyMode() + } + document.addEventListener("DOMContentLoaded", handler) + return () => { + document.removeEventListener("DOMContentLoaded", handler) + } + }, [dispatch]) + + // Renders UI on state.shouldRender. React.useLayoutEffect( React.useCallback(() => { // https://bugs.chromium.org/p/chromium/issues/detail?id=138439#c10 @@ -44,17 +56,19 @@ const Editor = ({ markup, children }) => { const domRange = Range.toDOMRange(state.range) domSelection.addRange(domRange) } catch (error) { - console.error(error) + console.error({ + range: state.range, + error, + }) } }) }, [state, dispatch]), [state.shouldRender], ) - // Exclusively returns a handler when the editor is - // unlocked; returns undefined when the editor is locked. - const newUnlockedHandler = handler => { - if (state.locked) { + // Exclusively returns a handler when state.readOnly=true. + const newReadWriteHandler = handler => { + if (state.readOnly) { return undefined } return handler @@ -78,19 +92,19 @@ const Editor = ({ markup, children }) => { tabSize: 4, }} - onFocus={newUnlockedHandler(e => { + onFocus={newReadWriteHandler(e => { dispatch.focus() })} - onBlur={newUnlockedHandler(e => { + onBlur={newReadWriteHandler(e => { dispatch.blur() })} - onPointerDown={newUnlockedHandler(e => { + onPointerDown={newReadWriteHandler(e => { pointerdownRef.current = true })} - onPointerMove={newUnlockedHandler(e => { + onPointerMove={newReadWriteHandler(e => { if (!state.focused) { pointerdownRef.current = false return @@ -107,13 +121,13 @@ const Editor = ({ markup, children }) => { dispatch.select(range) })} - onPointerUp={newUnlockedHandler(e => { + onPointerUp={newReadWriteHandler(e => { pointerdownRef.current = false })} // TODO: Add COMPAT guard for select-all or prevent // default? - onSelect={newUnlockedHandler(e => { + onSelect={newReadWriteHandler(e => { const range = Range.compute(ref.current) if (!range) { // No-op @@ -122,7 +136,7 @@ const Editor = ({ markup, children }) => { dispatch.select(range) })} - onKeyDown={newUnlockedHandler(e => { + onKeyDown={newReadWriteHandler(e => { const keydownT = keydown.detectType(e) if (keydownT) { console.log(keydownT) @@ -208,33 +222,33 @@ const Editor = ({ markup, children }) => { } })} - onInput={newUnlockedHandler(e => { + onInput={newReadWriteHandler(e => { const collapsed = Range.collapse(Range.compute(ref.current)) // Takes precedence const spans = Readers.rendered.spans(document.getElementById(collapsed[0].key)) dispatch.uncontrolledInputHandler(spans, collapsed) })} - onCut={newUnlockedHandler(e => { + onCut={newReadWriteHandler(e => { e.preventDefault() // TODO: e.clipboardData.setData("text/plain", ...) })} - onCopy={newUnlockedHandler(e => { + onCopy={newReadWriteHandler(e => { e.preventDefault() // TODO: e.clipboardData.setData("text/plain", ...) })} - onPaste={newUnlockedHandler(e => { + onPaste={newReadWriteHandler(e => { e.preventDefault() // TODO: e.clipboardData.getData("text/plain") })} - onDragStart={newUnlockedHandler(e => { + onDragStart={newReadWriteHandler(e => { e.preventDefault() })} - contentEditable={!state.locked} - suppressContentEditableWarning={!state.locked} + contentEditable={!state.readOnly} + suppressContentEditableWarning={!state.readOnly} /> {/* Debugger */} diff --git a/src/Editor/notes.js b/src/Editor/notes.js deleted file mode 100644 index 860bcd9..0000000 --- a/src/Editor/notes.js +++ /dev/null @@ -1,189 +0,0 @@ -import useMethods from "use-methods" - -// NOTE: Imports are intentionally unsorted. -import { - lock, - unlock, -} from "./lock" - -import { - focus, - blur, - select, -} from "./focus" - -import { - applyPlaintext, - applyEm, - applyStrong, - applyCode, - applyStrike, - applyA, -} from "./apply-inline" - -import { - insert, - uncontrolledInputHandler, -} from "./insert" - -import { - backspaceByRune, - backspaceByWord, - backspaceByLine, - deleteByRune, - deleteByWord, -} from "./backspace-delete" - -import { - cut, - copy, - paste, -} from "./clipboard" - -import { - pushUndoState, - undo, - redo, -} from "./history" - -// TODO: applyBlockFormat* -const dispatch = state => ({ - // Locks the editor; prevents future edits. Unlike blur, - // lock is expected to remove the DOM attribute - // contenteditable from the DOM tree. - lock() { - lock(state)() - }, - // Unlocks the editor; enables future edits. Unlike focus, - // unlock is expected to add the DOM attribute - // contenteditable to the DOM tree. - unlock() { - unlock(state)() - }, - // Focuses the editor; enables future edits. For example, - // when the editor is focused, undo and redo **are** - // expected to work. - focus() { - focus(state)() - }, - // Blurs the editor; prevents future edits. For example, - // when the editor is blurred, undo and redo **are not** - // expected to work. - blur() { - blur(state)() - }, - // Selects a range. Note that lock and blur are not - // expected to remove the current range from the editor - // internally. - select(range) { - select(state)(range) - }, - // Applies plaintext formatting to the current range. - applyPlaintext() { - applyPlaintext(state)() - }, - // Applies emphasis formatting to the current range. If - // the current range is already formatted as such, said - // formatting is expected to be removed. - applyEm() { - applyEm(state)() - }, - // Applies strong formatting to the current range. If the - // current range is already formatted as such, said - // formatting is expected to be removed. - applyStrong() { - applyStrong(state)() - }, - // Applies code formatting to the current range. If the - // current range is already formatted as such, said - // formatting is expected to be removed. - applyCode() { - applyCode(state)() - }, - // Applies strikethrough formatting to the current range. - // If the current range is already formatted as such, said - // formatting is expected to be removed. - applyStrike() { - applyStrike(state)() - }, - // Applies anchor formatting to the current range. If the - // current range is already formatted as such, said - // formatting is expected to be removed. - applyA(href) { - applyA(state)(href) - }, - // Inserts plaintext, HTML, or GitHub Flavored Markdown at - // the current range. mimeType can be "text/plaintext", - // "text/html", or "text/gfm". - insert(data, mimeType) { - insert(state)(data, mimeType) - }, - // Handler for uncontrolled input events. Uncontrolled - // means the event cannot be prevented. - uncontrolledInputHandler() { - uncontrolledInputHandler(state)() - }, - // Backspaces (deletes right-to-left) on the current range - // by one rune. - backspaceByRune() { - backspaceByRune(state)() - }, - // Backspaces (deletes right-to-left) on the current range - // by one word. - backspaceByWord() { - backspaceByWord(state)() - }, - // Backspaces (deletes right-to-left) on the current range - // by one line. - backspaceByLine() { - backspaceByLine(state)() - }, - // Deletes (deletes left-to-right) on the current range by - // one rune. - deleteByRune() { - deleteByRune(state)() - }, - // Deletes (deletes left-to-right) on the current range by - // one word. - deleteByWord() { - deleteByWord(state)() - }, - // Cuts the current range as plaintext, HTML, and GitHub - // Flavored Markdown to the editor clipboard. - cut() { - cut(state)() - }, - // Copies the current range as plaintext, HTML, and GitHub - // Flavored Markdown to the editor clipboard. - copy() { - copy(state)() - }, - // Pastes plaintext, HTML, or GitHub Flavored Markdown at - // the current range. mimeType can be "text/plaintext", - // "text/html", or "text/gfm". - paste(mimeType) { - paste(state)() - }, - // Pushes an undo state onto the history state stack. - pushUndoState(undoState) { - pushUndoState(state)(undoState) - }, - // Undos the editor history state stack once. - undo() { - undo(state)() - }, - // Redos the editor history state stack once. - redo() { - redo(state)() - }, -}) - -const init = ({ markup, children }) => ({ - // .... -}) - -function useEditor({ markup, children }) { - // ... -} - -export default useEditor diff --git a/src/Editor/useEditor/index.js b/src/Editor/useEditor/index.js index 89bdd80..b728d69 100644 --- a/src/Editor/useEditor/index.js +++ b/src/Editor/useEditor/index.js @@ -21,15 +21,11 @@ import useMethods from "use-methods" // this.select(collapsed) // render(state)() // }, -// input(spans, collapsed) { -// const element = state.elements.find(each => each.key === collapsed[0].key) -// element.props.spans = spans -// this.select(collapsed) -// render(state)() -// }, const init = elements => ({ - locked: false, + // NOTE: The DOM event "DOMContentLoaded" fires + // dispatch.enableReadOnlyMode. + readOnly: true, elements, focused: false, range: { diff --git a/src/Editor/useEditor/methods.js b/src/Editor/useEditor/methods.js index a054ffc..4d786e7 100644 --- a/src/Editor/useEditor/methods.js +++ b/src/Editor/useEditor/methods.js @@ -1,19 +1,14 @@ import $delete from "./delete" import applyFormat from "./applyFormat" -// TODO: Add findElementOrNode API or equivalent? const methods = state => ({ - // Locks the editor; disables future edits. Unlike blur, - // lock is expected to remove the DOM attribute - // contenteditable. - lock() { - state.locked = true - }, - // Unlocks the editor; enables future edits. Unlike focus, - // unlock is expected to add the DOM attribute - // contenteditable. - unlock() { - state.locked = false + // Enables read-only mode; disables future edits. + enableReadOnlyMode() { + state.readOnly = true + }, + // Disables read-only mode; enables future edits. + disableReadOnlyMode() { + state.readOnly = false }, // Focuses the editor. When the editor is focused, editing // operations are expected to work.