From 392fd75722eee19b704ff2c4f5060e79eb941e7b Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Wed, 29 Mar 2017 13:01:16 -0400 Subject: [PATCH] fix getPoint to work around in-editable content --- examples/check-lists/index.js | 38 ++++++++++++++++++++------ src/utils/get-point.js | 1 + src/utils/normalize-node-and-offset.js | 17 ++++++++---- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/examples/check-lists/index.js b/examples/check-lists/index.js index 63d941ab6..392c72732 100644 --- a/examples/check-lists/index.js +++ b/examples/check-lists/index.js @@ -96,8 +96,13 @@ class CheckLists extends React.Component { } /** - * On key down, if enter is pressed inside of a check list item, make sure - * that when it is split the new item starts unchecked. + * On key down... + * + * If enter is pressed inside of a check list item, make sure that when it + * is split the new item starts unchecked. + * + * If backspace is pressed when collapsed at the start of a check list item, + * then turn it back into a paragraph. * * @param {Event} e * @param {Object} data @@ -106,13 +111,28 @@ class CheckLists extends React.Component { */ onKeyDown = (e, data, state) => { - if (data.key != 'enter') return - if (state.startBlock.type != 'check-list-item') return - return state - .transform() - .splitBlock() - .setBlock({ data: { checked: false }}) - .apply() + if ( + data.key == 'enter' && + state.startBlock.type == 'check-list-item' + ) { + return state + .transform() + .splitBlock() + .setBlock({ data: { checked: false }}) + .apply() + } + + if ( + data.key == 'backspace' && + state.isCollapsed && + state.startBlock.type == 'check-list-item' && + state.selection.startOffset == 0 + ) { + return state + .transform() + .setBlock('paragraph') + .apply() + } } /** diff --git a/src/utils/get-point.js b/src/utils/get-point.js index d35f9b487..4a848f96c 100644 --- a/src/utils/get-point.js +++ b/src/utils/get-point.js @@ -1,3 +1,4 @@ + import OffsetKey from './offset-key' /** diff --git a/src/utils/normalize-node-and-offset.js b/src/utils/normalize-node-and-offset.js index 05aa0d611..858d9460b 100644 --- a/src/utils/normalize-node-and-offset.js +++ b/src/utils/normalize-node-and-offset.js @@ -15,13 +15,13 @@ function normalizeNodeAndOffset(node, offset) { const isLast = offset == node.childNodes.length const direction = isLast ? 'backward' : 'forward' const index = isLast ? offset - 1 : offset - node = getNonComment(node, index, direction) + node = getEditableChild(node, index, direction) // If the node has children, traverse until we have a leaf node. Leaf nodes // can be either text nodes, or other void DOM nodes. while (node.nodeType == 1 && node.childNodes.length) { const i = isLast ? node.childNodes.length - 1 : 0 - node = getNonComment(node, i, direction) + node = getEditableChild(node, i, direction) } // Determine the new offset inside the text node. @@ -33,7 +33,8 @@ function normalizeNodeAndOffset(node, offset) { } /** - * Get the nearest non-comment to `index` in a `parent`, preferring `direction`. + * Get the nearest editable child at `index` in a `parent`, preferring + * `direction`. * * @param {Element} parent * @param {Number} index @@ -41,14 +42,20 @@ function normalizeNodeAndOffset(node, offset) { * @return {Element|Null} */ -function getNonComment(parent, index, direction) { +function getEditableChild(parent, index, direction) { const { childNodes } = parent let child = childNodes[index] let i = index let triedForward = false let triedBackward = false - while (child.nodeType == 8) { + // While the child is a comment node, or an element node with no children, + // keep iterating to find a sibling non-void, non-comment node. + while ( + (child.nodeType == 8) || + (child.nodeType == 1 && child.childNodes.length == 0) || + (child.nodeType == 1 && child.getAttribute('contenteditable') == 'false') + ) { if (triedForward && triedBackward) break if (i >= childNodes.length) {