diff --git a/lib/index.js b/lib/index.js index 1ede8d5..e1452ca 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,9 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; - exports.default = blockBreakoutPlugin; var _draftJs = require('draft-js'); @@ -38,16 +35,25 @@ var defaults = { * @return {Object} Object defining the draft-js API methods */ function blockBreakoutPlugin() { - var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var breakoutBlockType = options.breakoutBlockType || defaults.breakoutBlockType; var breakoutBlocks = options.breakoutBlocks || defaults.breakoutBlocks; var doubleBreakoutBlocks = options.doubleBreakoutBlocks || defaults.doubleBreakoutBlocks; + // Warn if a block is redundantly specified + breakoutBlocks.concat(doubleBreakoutBlocks).reduce(function (acc, cur) { + if (acc[cur]) { + console.error('The block `' + cur + '` was redundantly specified in `breakoutBlocks`' + ' as well as `doubleBreakoutBlocks`. This is probably an error.'); + } + acc[cur] = true; + return acc; + }, {}); + return { handleReturn: function handleReturn(e, _ref) { - var getEditorState = _ref.getEditorState; - var setEditorState = _ref.setEditorState; + var getEditorState = _ref.getEditorState, + setEditorState = _ref.setEditorState; var editorState = getEditorState(); var currentBlockType = _draftJs.RichUtils.getCurrentBlockType(editorState); @@ -60,73 +66,67 @@ function blockBreakoutPlugin() { // Check if the selection is collapsed if (selection.isCollapsed()) { - var _ret = function () { - var contentState = editorState.getCurrentContent(); - var currentBlock = contentState.getBlockForKey(selection.getEndKey()); - var endOffset = selection.getEndOffset(); - var atEndOfBlock = endOffset === currentBlock.getLength(); - var atStartOfBlock = endOffset === 0; - - // Check we’re at the start/end of the current block - if (atEndOfBlock && isSingleBreakoutBlock || atStartOfBlock && isSingleBreakoutBlock || atStartOfBlock && !currentBlock.getLength()) { - var emptyBlockKey = (0, _draftJs.genKey)(); - var emptyBlock = new _draftJs.ContentBlock({ - key: emptyBlockKey, - text: '', - type: breakoutBlockType, - characterList: (0, _immutable.List)(), - depth: 0 - }); - var blockMap = contentState.getBlockMap(); - // Split the blocks - var blocksBefore = blockMap.toSeq().takeUntil(function (v) { - return v === currentBlock; - }); - - var blocksAfter = blockMap.toSeq().skipUntil(function (v) { - return v === currentBlock; - }).rest(); - - var augmentedBlocks = void 0; - var focusKey = void 0; - // Choose which order to apply the augmented blocks in depending - // on whether we’re at the start or the end - if (atEndOfBlock) { - if (isDoubleBreakoutBlock) { - // Discard Current as it was blank - augmentedBlocks = [[emptyBlockKey, emptyBlock]]; - } else { - // Current first, empty block afterwards - augmentedBlocks = [[currentBlock.getKey(), currentBlock], [emptyBlockKey, emptyBlock]]; - } - focusKey = emptyBlockKey; + var contentState = editorState.getCurrentContent(); + var currentBlock = contentState.getBlockForKey(selection.getEndKey()); + var endOffset = selection.getEndOffset(); + var atEndOfBlock = endOffset === currentBlock.getLength(); + var atStartOfBlock = endOffset === 0; + + // Check we’re at the start/end of the current block + if (atEndOfBlock && isSingleBreakoutBlock || atStartOfBlock && isSingleBreakoutBlock || atStartOfBlock && !currentBlock.getLength()) { + var emptyBlockKey = (0, _draftJs.genKey)(); + var emptyBlock = new _draftJs.ContentBlock({ + key: emptyBlockKey, + text: '', + type: breakoutBlockType, + characterList: (0, _immutable.List)(), + depth: 0 + }); + var blockMap = contentState.getBlockMap(); + // Split the blocks + var blocksBefore = blockMap.toSeq().takeUntil(function (v) { + return v === currentBlock; + }); + + var blocksAfter = blockMap.toSeq().skipUntil(function (v) { + return v === currentBlock; + }).rest(); + + var augmentedBlocks = void 0; + var focusKey = void 0; + // Choose which order to apply the augmented blocks in depending + // on whether we’re at the start or the end + if (atEndOfBlock) { + if (isSingleBreakoutBlock) { + // Current first, empty block afterwards + augmentedBlocks = [[currentBlock.getKey(), currentBlock], [emptyBlockKey, emptyBlock]]; } else { - // Empty first, current block afterwards - augmentedBlocks = [[emptyBlockKey, emptyBlock], [currentBlock.getKey(), currentBlock]]; - focusKey = currentBlock.getKey(); + // Discard Current as it was blank + augmentedBlocks = [[emptyBlockKey, emptyBlock]]; } - // Join back together with the current + new block - var newBlocks = blocksBefore.concat(augmentedBlocks, blocksAfter).toOrderedMap(); - var newContentState = contentState.merge({ - blockMap: newBlocks, - selectionBefore: selection, - selectionAfter: selection.merge({ - anchorKey: focusKey, - anchorOffset: 0, - focusKey: focusKey, - focusOffset: 0, - isBackward: false - }) - }); - // Set the state - setEditorState(_draftJs.EditorState.push(editorState, newContentState, 'split-block')); - return { - v: 'handled' - }; + focusKey = emptyBlockKey; + } else { + // Empty first, current block afterwards + augmentedBlocks = [[emptyBlockKey, emptyBlock], [currentBlock.getKey(), currentBlock]]; + focusKey = currentBlock.getKey(); } - }(); - - if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; + // Join back together with the current + new block + var newBlocks = blocksBefore.concat(augmentedBlocks, blocksAfter).toOrderedMap(); + var newContentState = contentState.merge({ + blockMap: newBlocks, + selectionBefore: selection, + selectionAfter: selection.merge({ + anchorKey: focusKey, + anchorOffset: 0, + focusKey: focusKey, + focusOffset: 0, + isBackward: false + }) + }); + // Set the state + setEditorState(_draftJs.EditorState.push(editorState, newContentState, 'split-block')); + return 'handled'; + } } } return 'not-handled'; diff --git a/src/index.js b/src/index.js index a29103c..f01e853 100644 --- a/src/index.js +++ b/src/index.js @@ -48,6 +48,17 @@ export default function blockBreakoutPlugin (options = {}) { const breakoutBlocks = options.breakoutBlocks || defaults.breakoutBlocks const doubleBreakoutBlocks = options.doubleBreakoutBlocks || defaults.doubleBreakoutBlocks + // Warn if a block is redundantly specified + breakoutBlocks.concat(doubleBreakoutBlocks).reduce((acc, cur) => { + if (acc[cur]) { + console.error( + 'The block `' + cur + '` was redundantly specified in `breakoutBlocks`' + + ' as well as `doubleBreakoutBlocks`. This is probably an error.') + } + acc[cur] = true + return acc + }, {}) + return { handleReturn (e, { getEditorState, setEditorState }) { const editorState = getEditorState() @@ -92,15 +103,15 @@ export default function blockBreakoutPlugin (options = {}) { // Choose which order to apply the augmented blocks in depending // on whether we’re at the start or the end if (atEndOfBlock) { - if (isDoubleBreakoutBlock) { - // Discard Current as it was blank + if (isSingleBreakoutBlock) { + // Current first, empty block afterwards augmentedBlocks = [ + [currentBlock.getKey(), currentBlock], [emptyBlockKey, emptyBlock], ] } else { - // Current first, empty block afterwards + // Discard Current as it was blank augmentedBlocks = [ - [currentBlock.getKey(), currentBlock], [emptyBlockKey, emptyBlock], ] } diff --git a/test/index.js b/test/index.js index 3fd7c0c..91166b2 100644 --- a/test/index.js +++ b/test/index.js @@ -1,5 +1,6 @@ import test from 'tape' import isFunction from 'is-function' +import {convertToRaw, ContentBlock, ContentState, EditorState} from 'draft-js' import createBlockBreakoutPlugin from '../src' test('it should create a draft-js plugin', (nest) => { @@ -10,3 +11,65 @@ test('it should create a draft-js plugin', (nest) => { assert.end() }) }) + +test('it should break out of `breakoutBlocks`', (t) => { + const {handleReturn} = createBlockBreakoutPlugin() + const contentState = ContentState.createFromBlockArray([new ContentBlock({ + key: '1', + type: 'header-one', + text: 'test', + })]) + let editorState = EditorState.moveSelectionToEnd( + EditorState.createWithContent(contentState)) + const getEditorState = () => editorState + const setEditorState = (state) => { editorState = state } + t.equal(handleReturn(undefined, {getEditorState, setEditorState}), 'handled') + const serialized = convertToRaw(editorState.getCurrentContent()) + t.equal(serialized.blocks.length, 2) + t.equal(serialized.blocks[0].type, 'header-one') + t.equal(serialized.blocks[0].text, 'test') + t.equal(serialized.blocks[1].type, 'unstyled') + t.equal(serialized.blocks[1].text, '') + t.end() +}) + +test('it prints an error when a block is redundantly specified', (t) => { + const originalConsoleError = console.error + const errors = [] + console.error = (e) => errors.push(e) + createBlockBreakoutPlugin({ + breakoutBlocks: ['blockquote'], + doubleBreakoutBlocks: ['blockquote'], + }) + t.equal(errors.length, 1) + t.equal(errors[0], 'The block `blockquote` was redundantly specified in `breakoutBlocks` as well as `doubleBreakoutBlocks`. This is probably an error.') + t.end() + console.error = originalConsoleError +}) + +test('it treats a block is a regular breakout block if it is redundantly specified', (t) => { + const originalConsoleError = console.error + console.error = () => {} + const {handleReturn} = createBlockBreakoutPlugin({ + breakoutBlocks: ['blockquote'], + doubleBreakoutBlocks: ['blockquote'], + }) + const contentState = ContentState.createFromBlockArray([new ContentBlock({ + key: '1', + type: 'blockquote', + text: 'test', + })]) + let editorState = EditorState.moveSelectionToEnd( + EditorState.createWithContent(contentState)) + const getEditorState = () => editorState + const setEditorState = (state) => { editorState = state } + t.equal(handleReturn(undefined, {getEditorState, setEditorState}), 'handled') + const serialized = convertToRaw(editorState.getCurrentContent()) + t.equal(serialized.blocks.length, 2) + t.equal(serialized.blocks[0].type, 'blockquote') + t.equal(serialized.blocks[0].text, 'test') + t.equal(serialized.blocks[1].type, 'unstyled') + t.equal(serialized.blocks[1].text, '') + t.end() + console.error = originalConsoleError +})