diff --git a/src/lib/rich-text/extensions/code-plugin/changes/dedent-lines.js b/src/lib/rich-text/extensions/code-plugin/changes/dedent-lines.js deleted file mode 100644 index 2ffd03a..0000000 --- a/src/lib/rich-text/extensions/code-plugin/changes/dedent-lines.js +++ /dev/null @@ -1,31 +0,0 @@ -// Return the index of the first character that differs between both string, or -// the smallest string length otherwise. -function firstDifferentCharacter(one, two) { - if (one.length > two.length) { - return firstDifferentCharacter(two, one) - } - - const indexes = new Array(one.length).fill().map((v, i) => i) - const index = indexes.find((i) => one[i] !== two[i]) - - return index == null ? one.length : index -} - -/** - * Dedent all lines in selection - */ -export function dedentLines(opts, change, indent) { - const { value } = change - const { document, selection } = value - const lines = document - .getLeafBlocksAtRange(selection) - .filter((node) => node.type === opts.line) - - return lines.reduce((c, line) => { - // Remove a level of indent from the start of line - const textNode = line.nodes.first() - const lengthToRemove = firstDifferentCharacter(textNode.text, indent) - - return c.removeTextByKey(textNode.key, 0, lengthToRemove) - }, change) -} diff --git a/src/lib/rich-text/extensions/code-plugin/changes/index.js b/src/lib/rich-text/extensions/code-plugin/changes/index.js index 9065a5a..aa8062d 100644 --- a/src/lib/rich-text/extensions/code-plugin/changes/index.js +++ b/src/lib/rich-text/extensions/code-plugin/changes/index.js @@ -1,15 +1 @@ -import { indentLines } from "./indent-lines" -import { dedentLines } from "./dedent-lines" -import { unwrapCodeBlock } from "./unwrap-code-block" -import { unwrapCodeBlockByKey } from "./unwrap-code-block-by-key" -import { wrapCodeBlock } from "./wrap-code-block" -import { wrapCodeBlockByKey } from "./wrap-code-block-by-key" - -export { - dedentLines, - indentLines, - unwrapCodeBlock, - unwrapCodeBlockByKey, - wrapCodeBlock, - wrapCodeBlockByKey, -} +export { indentLines } from "./indent-lines" diff --git a/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block-by-key.js b/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block-by-key.js deleted file mode 100644 index b9f14c5..0000000 --- a/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block-by-key.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Unwrap a code block into a normal block. - */ -export function unwrapCodeBlockByKey(opts, change, key, type) { - const { value } = change - const { document } = value - - // Get the code block - const codeBlock = document.getDescendant(key) - - if (!codeBlock || codeBlock.type !== opts.containerType) { - throw new Error( - "Block passed to unwrapCodeBlockByKey should be a code block container", - ) - } - - // change lines into paragraph - codeBlock.nodes.forEach((line) => - change - .setNodeByKey(line.key, { type }, { normalize: false }) - .unwrapNodeByKey(line.key, { normalize: false }), - ) - - return change -} diff --git a/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block.js b/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block.js deleted file mode 100644 index f6770a4..0000000 --- a/src/lib/rich-text/extensions/code-plugin/changes/unwrap-code-block.js +++ /dev/null @@ -1,21 +0,0 @@ -import { getCurrentCode } from "../utils" - -import { unwrapCodeBlockByKey } from "./unwrap-code-block-by-key" - -/** - * Convert a code block to a normal block. - */ -export function unwrapCodeBlock(opts, change, type) { - const { value } = change - - const codeBlock = getCurrentCode(opts, value) - - if (!codeBlock) { - return change - } - - // Convert to paragraph - unwrapCodeBlockByKey(opts, change, codeBlock.key, type) - - return change -} diff --git a/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block-by-key.js b/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block-by-key.js deleted file mode 100644 index d93c6c7..0000000 --- a/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block-by-key.js +++ /dev/null @@ -1,31 +0,0 @@ -import { deserializeCode } from "../utils" - -/** - * Wrap a block into a code block. - */ -export function wrapCodeBlockByKey(opts, change, key) { - const { value } = change - const { document } = value - - const startBlock = document.getDescendant(key) - const { text } = startBlock - - // Remove all child - startBlock.nodes.forEach((node) => { - change.removeNodeByKey(node.key, { normalize: false }) - }) - - // Insert new text - const toInsert = deserializeCode(opts, text) - - toInsert.nodes.forEach((node, i) => { - change.insertNodeByKey(startBlock.key, i, node, { normalize: false }) - }) - - // Set node type - change.setNodeByKey(startBlock.key, { - type: opts.containerType, - }) - - return change -} diff --git a/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block.js b/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block.js deleted file mode 100644 index 3044f6b..0000000 --- a/src/lib/rich-text/extensions/code-plugin/changes/wrap-code-block.js +++ /dev/null @@ -1,19 +0,0 @@ -import { wrapCodeBlockByKey } from "./wrap-code-block-by-key" - -/** - * Wrap current block into a code block. - */ -export function wrapCodeBlock(opts, change) { - const { value } = change - const { startBlock, selection } = value - - // Convert to code block - wrapCodeBlockByKey(opts, change, startBlock.key) - - // Move selection back in the block - change - .moveToStartOfNode(change.value.document.getDescendant(startBlock.key)) - .moveAnchorTo(selection.startOffset) - - return change -} diff --git a/src/lib/rich-text/extensions/code-plugin/core.js b/src/lib/rich-text/extensions/code-plugin/core.js deleted file mode 100644 index 173526c..0000000 --- a/src/lib/rich-text/extensions/code-plugin/core.js +++ /dev/null @@ -1,36 +0,0 @@ -import { Options } from "./options" -import { deserializeCode } from "./utils" -import { - wrapCodeBlockByKey, - unwrapCodeBlockByKey, - wrapCodeBlock, - unwrapCodeBlock, -} from "./changes" - -/** - * The core of the plugin, which does not relies on `slate-react`, and includes - * everything but behavior and rendering logic. - */ -export function core(optsParam) { - const opts = new Options(optsParam) - - return { - changes: { - unwrapCodeBlockByKey: (change, key, type) => - unwrapCodeBlockByKey(opts, change, key, type), - - wrapCodeBlockByKey: (change, key, type) => - wrapCodeBlockByKey(opts, change, key, type), - - wrapCodeBlock: (change, key, type) => - wrapCodeBlock(opts, change, key, type), - - unwrapCodeBlock: (change, key, type) => - unwrapCodeBlock(opts, change, key, type), - }, - - utils: { - deserializeCode: (text) => deserializeCode(opts, text), - }, - } -} diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/index.js b/src/lib/rich-text/extensions/code-plugin/handlers/index.js index f332b47..a6f4e7d 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/index.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/index.js @@ -1,5 +1,4 @@ export { onTab } from "./on-tab" -export { onShiftTab } from "./on-shift-tab" export { onEnter } from "./on-enter" export { onModEnter } from "./on-mod-enter" export { onBackspace } from "./on-backspace" diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-backspace.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-backspace.js index 92941d0..566ff67 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-backspace.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-backspace.js @@ -1,4 +1,5 @@ -import { getCurrentIndent, getCurrentCode } from "../utils" +import { getCurrentIndent } from "../utils" +import { getCurrentCode } from "../../common/utils" /** * User pressed Delete in an editor: @@ -31,12 +32,11 @@ export function onBackspace(opts, event, change, editor) { } if (opts.exitBlockType) { // Otherwise check if we are in an empty code container... - const currentCode = getCurrentCode(opts, value) + const codeBlock = getCurrentCode(opts, value) const isStartOfCode = - selection.start.offset === 0 && currentCode.getFirstText() === startText - // PERF: avoid checking for whole currentCode.text - const isEmpty = - currentCode.nodes.size === 1 && currentLine.text.length === 0 + selection.start.offset === 0 && codeBlock.getFirstText() === startText + // PERF: avoid checking for whole codeBlock.text + const isEmpty = codeBlock.nodes.size === 1 && currentLine.text.length === 0 if (isStartOfCode && isEmpty) { event.preventDefault() diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-key-down.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-key-down.js index a837786..079de81 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-key-down.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-key-down.js @@ -1,14 +1,12 @@ import { isKeyHotkey } from "is-hotkey" -import { getCurrentCode } from "../utils" +import { getCurrentCode } from "../../common/utils" import { onTab } from "./on-tab" -import { onShiftTab } from "./on-shift-tab" import { onEnter } from "./on-enter" import { onModEnter } from "./on-mod-enter" import { onBackspace } from "./on-backspace" import { onSelectAll } from "./on-select-all" const isModA = isKeyHotkey("mod+a") -const isShiftTab = isKeyHotkey("shift+tab") const isTab = isKeyHotkey("tab") const isModZ = isKeyHotkey("mod+z") const isModEnter = isKeyHotkey("mod+enter") @@ -20,11 +18,11 @@ const isBackspace = isKeyHotkey("backspace") */ export function onKeyDown(opts, event, change, editor) { const { value } = change - const currentCode = getCurrentCode(opts, value) + const codeBlock = getCurrentCode(opts, value) // Inside code ? - if (!currentCode) { + if (!codeBlock) { return editor() } @@ -35,10 +33,7 @@ export function onKeyDown(opts, event, change, editor) { if (opts.selectAll && isModA(event)) { return onSelectAll(...args) } - if (isShiftTab(event)) { - // User is pressing Shift+Tab - return onShiftTab(...args) - } + if (isTab(event)) { // User is pressing Tab return onTab(...args) diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-mod-enter.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-mod-enter.js index 4f7dd38..90c7a1f 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-mod-enter.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-mod-enter.js @@ -15,15 +15,11 @@ export function onModEnter(opts, event, change, editor) { event.preventDefault() - // Default behavior: insert an exit block - const range = change.value.selection - const exitBlock = Block.create({ type: "paragraph", nodes: [Text.create()], }) - change.deleteAtRange(range, { normalize: false }) change.insertBlockAtRange(selection, exitBlock, { normalize: false, }) diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-paste.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-paste.js index 01e3fda..ed12bd0 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-paste.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-paste.js @@ -1,6 +1,7 @@ import { Document } from "slate" import { getEventTransfer } from "slate-react" -import { getCurrentCode, deserializeCode } from "../utils" +import { deserializeCode } from "../utils" +import { getCurrentCode } from "../../common/utils" /** * User is pasting content, insert it as text @@ -8,12 +9,12 @@ import { getCurrentCode, deserializeCode } from "../utils" export function onPaste(opts, event, change, editor) { const { value } = change const data = getEventTransfer(event) - const currentCode = getCurrentCode(opts, value) + const codeBlock = getCurrentCode(opts, value) // Only handle paste when selection is completely a code block const { endBlock } = value - if (!currentCode || !currentCode.hasDescendant(endBlock.key)) { + if (!codeBlock || !codeBlock.hasDescendant(endBlock.key)) { return editor() } diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-select-all.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-select-all.js index de01944..3234610 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-select-all.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-select-all.js @@ -1,16 +1,16 @@ -import { getCurrentCode } from "../utils" +import { getCurrentCode } from "../../common/utils" /** * User is Cmd+A to select all text */ -export function onSelectAll(opts, event, change, editor) { +export function onSelectAll(opts, event, change) { const { value } = change event.preventDefault() - const currentCode = getCurrentCode(opts, value) + const codeBlock = getCurrentCode(opts, value) return change - .moveToStartOfNode(currentCode.getFirstText()) - .moveFocusToEndOfNode(currentCode.getLastText()) + .moveToStartOfNode(codeBlock.getFirstText()) + .moveFocusToEndOfNode(codeBlock.getLastText()) } diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-shift-tab.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-shift-tab.js deleted file mode 100644 index 320cf6d..0000000 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-shift-tab.js +++ /dev/null @@ -1,18 +0,0 @@ -import { getCurrentIndent } from "../utils" -import { dedentLines } from "../changes" - -/** - * User pressed Shift+Tab in an editor: - * Reduce indentation in the selected lines. - */ -export function onShiftTab(opts, event, change, editor) { - const { value } = change - - event.preventDefault() - event.stopPropagation() - - const indent = getCurrentIndent(opts, value) - - // We dedent all selected lines - return dedentLines(opts, change, indent) -} diff --git a/src/lib/rich-text/extensions/code-plugin/handlers/on-tab.js b/src/lib/rich-text/extensions/code-plugin/handlers/on-tab.js index 9809c49..a9e594f 100644 --- a/src/lib/rich-text/extensions/code-plugin/handlers/on-tab.js +++ b/src/lib/rich-text/extensions/code-plugin/handlers/on-tab.js @@ -5,7 +5,7 @@ import { indentLines } from "../changes" * User pressed Tab in an editor: * Insert a tab after detecting it from code block content. */ -export function onTab(opts, event, change, editor) { +export function onTab(opts, event, change) { const { value } = change event.preventDefault() diff --git a/src/lib/rich-text/extensions/code-plugin/index.js b/src/lib/rich-text/extensions/code-plugin/index.js index feb74a1..9a89ce6 100644 --- a/src/lib/rich-text/extensions/code-plugin/index.js +++ b/src/lib/rich-text/extensions/code-plugin/index.js @@ -1,20 +1,37 @@ import React from "react" import { PrismPlugin } from "./prism-plugin" import { CodeBlock } from "./code-block" - import { Options } from "./options" import { onKeyDown, onPaste } from "./handlers" -import { core } from "./core" export const CodePlugin = (options = {}) => { const config = new Options({ ...options, }) - const getCore = core(config) - return [ { + commands: { + insertCode(editor, code) { + editor.insertBlock({ + object: "block", + type: config.block, + nodes: [ + { + object: "block", + type: config.line, + nodes: [{ object: "text", leaves: [code] }], + }, + ], + }) + }, + }, + + onKeyDown: (event, change, editor) => + onKeyDown(config, event, change, editor), + + onPaste: (event, change, editor) => + onPaste(config, event, change, editor), renderNode(props, editor, next) { const { node, children, attributes } = props @@ -29,18 +46,10 @@ export const CodePlugin = (options = {}) => { return Types[node.type] || next() }, - ...getCore, }, PrismPlugin({ onlyIn: (node) => node.type === config.block, getSyntax: (node) => node.data.get("language"), }), - { - onKeyDown: (event, change, editor) => - onKeyDown(config, event, change, editor), - - onPaste: (event, change, editor) => - onPaste(config, event, change, editor), - }, ] } diff --git a/src/lib/rich-text/extensions/code-plugin/options.js b/src/lib/rich-text/extensions/code-plugin/options.js index a64a124..ca78c8a 100644 --- a/src/lib/rich-text/extensions/code-plugin/options.js +++ b/src/lib/rich-text/extensions/code-plugin/options.js @@ -5,7 +5,6 @@ const DEFAULTS = { line: "", exitBlockType: "paragraph", selectAll: true, - allowMarks: false, getIndent: null, } diff --git a/src/lib/rich-text/extensions/code-plugin/utils/deserialize-code.js b/src/lib/rich-text/extensions/code-plugin/utils/deserialize-code.js index b48a374..f5b199d 100644 --- a/src/lib/rich-text/extensions/code-plugin/utils/deserialize-code.js +++ b/src/lib/rich-text/extensions/code-plugin/utils/deserialize-code.js @@ -7,7 +7,7 @@ const DEFAULT_NEWLINE = "\n" /** * Deserialize a text into a code block */ -export function deserializeCode(opts, text) { +export const deserializeCode = (opts, text) => { const sep = detectNewline(text) || DEFAULT_NEWLINE const lines = List(text.split(sep)).map((line) => diff --git a/src/lib/rich-text/extensions/code-plugin/utils/get-current-indent.js b/src/lib/rich-text/extensions/code-plugin/utils/get-current-indent.js index 9275bb3..f364382 100644 --- a/src/lib/rich-text/extensions/code-plugin/utils/get-current-indent.js +++ b/src/lib/rich-text/extensions/code-plugin/utils/get-current-indent.js @@ -1,5 +1,5 @@ +import { getCurrentCode } from "../../common/utils" import { getIndent } from "./get-indent" -import { getCurrentCode } from "./get-current-code" /** * Detect indentation in the current code block diff --git a/src/lib/rich-text/extensions/code-plugin/utils/index.js b/src/lib/rich-text/extensions/code-plugin/utils/index.js index d3dce87..023f5f9 100644 --- a/src/lib/rich-text/extensions/code-plugin/utils/index.js +++ b/src/lib/rich-text/extensions/code-plugin/utils/index.js @@ -1,6 +1,3 @@ -import { deserializeCode } from "./deserialize-code" -import { getCurrentCode } from "./get-current-code" -import { getCurrentIndent } from "./get-current-indent" -import { getIndent } from "./get-indent" - -export { deserializeCode, getCurrentCode, getCurrentIndent, getIndent } +export { getCurrentIndent } from "./get-current-indent" +export { getIndent } from "./get-indent" +export { deserializeCode } from "./deserialize-code" diff --git a/src/lib/rich-text/extensions/code-plugin/utils/get-current-code.js b/src/lib/rich-text/extensions/common/utils/get-current-code.js similarity index 100% rename from src/lib/rich-text/extensions/code-plugin/utils/get-current-code.js rename to src/lib/rich-text/extensions/common/utils/get-current-code.js diff --git a/src/lib/rich-text/extensions/common/utils/index.js b/src/lib/rich-text/extensions/common/utils/index.js new file mode 100644 index 0000000..bb87b62 --- /dev/null +++ b/src/lib/rich-text/extensions/common/utils/index.js @@ -0,0 +1 @@ +export { getCurrentCode } from "./get-current-code" diff --git a/src/lib/rich-text/extensions/hover-menu/changes/index.js b/src/lib/rich-text/extensions/hover-menu/changes/index.js new file mode 100644 index 0000000..e2939cb --- /dev/null +++ b/src/lib/rich-text/extensions/hover-menu/changes/index.js @@ -0,0 +1 @@ +export { toggleCodeBlock } from "./toggle-code-block" diff --git a/src/lib/rich-text/extensions/hover-menu/changes/is-in-code-block.js b/src/lib/rich-text/extensions/hover-menu/changes/is-in-code-block.js new file mode 100644 index 0000000..4e2d78e --- /dev/null +++ b/src/lib/rich-text/extensions/hover-menu/changes/is-in-code-block.js @@ -0,0 +1,12 @@ +/** + * Test if current selection is in a code block. + */ +export const isInCodeBlock = (opts, value) => { + const { document, selection } = value + const codeBlock = document.getClosest( + selection.start.key, + (block) => block.type === opts.block, + ) + + return Boolean(codeBlock) +} diff --git a/src/lib/rich-text/extensions/hover-menu/changes/toggle-code-block.js b/src/lib/rich-text/extensions/hover-menu/changes/toggle-code-block.js new file mode 100644 index 0000000..ab8238c --- /dev/null +++ b/src/lib/rich-text/extensions/hover-menu/changes/toggle-code-block.js @@ -0,0 +1,13 @@ +import { isInCodeBlock } from "./is-in-code-block" +import { unwrapCodeBlock } from "./unwrap-code-block" +import { wrapCodeBlock } from "./wrap-code-block" + +/** + * Toggle code block / paragraph. + */ +export const toggleCodeBlock = (opts, change, type) => { + if (isInCodeBlock(opts, change.value)) { + return unwrapCodeBlock(opts, change, type) + } + return wrapCodeBlock(opts, change) +} diff --git a/src/lib/rich-text/extensions/hover-menu/changes/unwrap-code-block.js b/src/lib/rich-text/extensions/hover-menu/changes/unwrap-code-block.js new file mode 100644 index 0000000..190b9e8 --- /dev/null +++ b/src/lib/rich-text/extensions/hover-menu/changes/unwrap-code-block.js @@ -0,0 +1,39 @@ +import { getCurrentCode } from "../../common/utils" + +/** + * Convert a code block to a normal block. + */ +export function unwrapCodeBlock(opts, change, type) { + const { value } = change + + const codeBlock = getCurrentCode(opts, value) + + if (!codeBlock) { + return change + } + + // Convert to paragraph + unwrapCodeBlockByKey(opts, change, codeBlock.key, type) + + return change +} + +/** + * Unwrap a code block into a normal block. + */ +function unwrapCodeBlockByKey(opts, change, key, type) { + const { value } = change + const { document } = value + + // Get the code block + const codeBlock = document.getDescendant(key) + + // change lines into paragraph + codeBlock.nodes.forEach((line) => + change + .setNodeByKey(line.key, { type }, { normalize: false }) + .unwrapNodeByKey(line.key, { normalize: false }), + ) + + return change +} diff --git a/src/lib/rich-text/extensions/hover-menu/changes/wrap-code-block.js b/src/lib/rich-text/extensions/hover-menu/changes/wrap-code-block.js new file mode 100644 index 0000000..2ccd64a --- /dev/null +++ b/src/lib/rich-text/extensions/hover-menu/changes/wrap-code-block.js @@ -0,0 +1,16 @@ +/** + * Wrap current block into a code block. + */ +export function wrapCodeBlock(opts, change) { + const { value } = change + + let newTexts = [] + + value.texts.forEach((node) => { + newTexts = [...newTexts, node.text] + }) + newTexts = newTexts.join("\n") + change.insertCode(newTexts) + + return change +} diff --git a/src/lib/rich-text/extensions/hover-menu/index.js b/src/lib/rich-text/extensions/hover-menu/index.js index dcb2ef8..6c3e47d 100644 --- a/src/lib/rich-text/extensions/hover-menu/index.js +++ b/src/lib/rich-text/extensions/hover-menu/index.js @@ -2,58 +2,11 @@ import React from "react" import ReactDOM from "react-dom" import { DEFAULT_NODE, ICONS_LIST } from "./constant" import { StyledMenu, Button } from "./styles" +import { toggleCodeBlock } from "./changes" const getIconComponent = (type) => ICONS_LIST[type] || "span" export class HoverMenu extends React.Component { - handleCode = (type) => { - const { editor } = this.props - const { value } = editor - const { document } = value - - // Handle everything but list buttons. - if (type !== "code") { - const isActive = this.hasBlock(type) - const isCodeLine = this.hasBlock("code_line") - - if (isCodeLine) { - editor - .setBlocks(isActive ? DEFAULT_NODE : type) - .unwrapBlock("bulleted-list") - .unwrapBlock("numbered-list") - } - } - - if (type === "code") { - // Handle the extra wrapping required for list buttons. - const isCodeLine = this.hasBlock("code_line") - const isType = value.blocks.some((block) => - Boolean( - document.getClosest(block.key, (parent) => parent.type === type), - ), - ) - - if (isCodeLine && isType) { - editor - .setBlocks(DEFAULT_NODE) - .unwrapBlock(type) - .unwrapBlock("bulleted-list") - .unwrapBlock("numbered-list") - } else if (isCodeLine) { - editor - .unwrapBlock("bulleted-list") - .unwrapBlock("numbered-list") - .wrapBlock(type) - } else { - editor - .setBlocks("code_line") - .unwrapBlock("bulleted-list") - .unwrapBlock("numbered-list") - .wrapBlock(type) - } - } - } - handleLists = (type) => { const { editor } = this.props const { value } = editor @@ -100,18 +53,15 @@ export class HoverMenu extends React.Component { onClickBlock = (event, type) => { event.preventDefault() - const { editor } = this.props - const { value } = editor - const { document } = value + const { editor, configCodeBlock } = this.props this.handleLists(type) - this.handleCode(type) - if ( - type !== "bulleted-list" && - type !== "numbered-list" && - type !== "code" - ) { + if (type === configCodeBlock.block) { + toggleCodeBlock(configCodeBlock, editor, type) + } + + if (type === "block-quote") { const isActive = this.hasBlock(type) editor.setBlocks(isActive ? DEFAULT_NODE : type) @@ -123,12 +73,10 @@ export class HoverMenu extends React.Component { if (["numbered-list", "bulleted-list"].includes(type)) { const { editor } = this.props - const { - value: { document, blocks }, - } = editor + const { value } = editor - if (blocks.size > 0) { - const parent = document.getParent(blocks.first().key) + if (value.blocks.size > 0) { + const parent = value.document.getParent(value.blocks.first().key) isActive = this.hasBlock("list-item") && parent && parent.type === type } @@ -188,7 +136,7 @@ export class HoverMenu extends React.Component { } render() { - const { className, innerRef } = this.props + const { className, innerRef, configCodeBlock } = this.props const root = window.document.getElementById("root") return ReactDOM.createPortal( @@ -196,7 +144,7 @@ export class HoverMenu extends React.Component { {this.renderMarkButton("bold")} {this.renderMarkButton("italic")} {this.renderMarkButton("underlined")} - {this.renderBlockButton("code")} + {configCodeBlock.block && this.renderBlockButton(configCodeBlock.block)} {this.renderBlockButton("block-quote")} {this.renderBlockButton("numbered-list")} {this.renderBlockButton("bulleted-list")} diff --git a/src/lib/rich-text/rich-editor.js b/src/lib/rich-text/rich-editor.js index c5ffcb7..d6db0c2 100644 --- a/src/lib/rich-text/rich-editor.js +++ b/src/lib/rich-text/rich-editor.js @@ -5,12 +5,12 @@ import Plain from "slate-plain-serializer" import { HoverMenu, CodePlugin } from "./extensions" import { RichEditorStyle } from "./styles" -const plugins = [ - CodePlugin({ - block: "code", - line: "code_line", - }), -] +const configCodeBlock = { + block: "code", + line: "code_line", +} + +const plugins = [CodePlugin(configCodeBlock)] export const NODES_COMPONENTS = { "block-quote": "blockquote", @@ -102,6 +102,7 @@ export class RichEditor extends React.Component { {children} (this.menu = menu)} editor={editor} @@ -112,7 +113,7 @@ export class RichEditor extends React.Component { render() { const { value } = this.state - const { content, readOnly } = this.props + const { readOnly } = this.props return ( <>