From 420d38edec32ed6616e60a52af420fb558763f84 Mon Sep 17 00:00:00 2001 From: Ian Storm Taylor Date: Mon, 20 Jun 2016 13:21:24 -0700 Subject: [PATCH] bug fixing --- examples/basic/index.js | 5 ++++- examples/plaintext/index.js | 3 +++ examples/richtext/index.js | 3 +++ lib/components/text.js | 24 +++++++----------------- lib/models/node.js | 21 +++++++++++++++++++++ lib/models/selection.js | 4 ++-- lib/models/state.js | 6 +++++- lib/serializers/plaintext.js | 8 ++++---- lib/utils/group-by-marks.js | 10 ++++++++-- lib/utils/offset-key.js | 15 ++++++++++++--- 10 files changed, 69 insertions(+), 30 deletions(-) diff --git a/examples/basic/index.js b/examples/basic/index.js index fd6446c15..b8b71b858 100644 --- a/examples/basic/index.js +++ b/examples/basic/index.js @@ -66,8 +66,11 @@ class App extends React.Component { renderMark={mark => this.renderMark(mark)} state={this.state.state} onChange={(state) => { - console.log('State:', state.toJS()) + console.groupCollapsed('Change!') + console.log('Document:', state.document.toJS()) + console.log('Selection:', state.selection.toJS()) console.log('Content:', Raw.serialize(state)) + console.groupEnd() this.setState({ state }) }} /> diff --git a/examples/plaintext/index.js b/examples/plaintext/index.js index 5fce0f0ef..75ae74608 100644 --- a/examples/plaintext/index.js +++ b/examples/plaintext/index.js @@ -25,8 +25,11 @@ class App extends React.Component { { + console.groupCollapsed('Change!') console.log('Document:', state.document.toJS()) + console.log('Selection:', state.selection.toJS()) console.log('Content:', Plaintext.serialize(state)) + console.groupEnd() this.setState({ state }) }} /> diff --git a/examples/richtext/index.js b/examples/richtext/index.js index 0c615b035..dd5b6267b 100644 --- a/examples/richtext/index.js +++ b/examples/richtext/index.js @@ -128,8 +128,11 @@ class App extends React.Component { renderNode={node => this.renderNode(node)} renderMark={mark => this.renderMark(mark)} onChange={(state) => { + console.groupCollapsed('Change!') console.log('Document:', state.document.toJS()) + console.log('Selection:', state.selection.toJS()) console.log('Content:', Raw.serialize(state)) + console.groupEnd() this.setState({ state }) }} /> diff --git a/lib/components/text.js b/lib/components/text.js index c3801ed76..ad009821c 100644 --- a/lib/components/text.js +++ b/lib/components/text.js @@ -34,15 +34,13 @@ class Text extends React.Component { const { node } = this.props const { characters } = node const ranges = groupByMarks(characters) - return ranges.size == 0 - ? this.renderSpacerLeaf() - : ranges.map((range, i, ranges) => { - const previous = ranges.slice(0, i) - const offset = previous.size - ? previous.map(range => range.text).join('').length - : 0 - return this.renderLeaf(range, offset) - }) + return ranges.map((range, i, ranges) => { + const previous = ranges.slice(0, i) + const offset = previous.size + ? previous.map(range => range.text).join('').length + : 0 + return this.renderLeaf(range, offset) + }) } renderLeaf(range, offset) { @@ -71,14 +69,6 @@ class Text extends React.Component { ) } - renderSpacerLeaf() { - return this.renderLeaf({ - marks: new List(), - offset: 0, - text: '' - }) - } - } /** diff --git a/lib/models/node.js b/lib/models/node.js index d2f98d194..b43ddb359 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -242,6 +242,27 @@ const Node = { return list }, + /** + * Get the first text child node. + * + * @return {Text or Null} text + */ + + getFirstTextNode() { + return this.findNode(node => node.type == 'text') || null + }, + + /** + * Get the last text child node. + * + * @return {Text or Null} text + */ + + getLastTextNode() { + const texts = this.findNode(node => node.type == 'text') + return texts.size ? texts.get(texts.size - 1) : null + }, + /** * Get a set of the marks in a `range`. * diff --git a/lib/models/selection.js b/lib/models/selection.js index af4645865..aa82ed117 100644 --- a/lib/models/selection.js +++ b/lib/models/selection.js @@ -109,7 +109,7 @@ class Selection extends SelectionRecord { isAtStartOf(node) { const { startKey, startOffset } = this - const first = node.type == 'text' ? node : node.nodes.first() + const first = node.type == 'text' ? node : node.getFirstTextNode() return startKey == first.key && startOffset == 0 } @@ -122,7 +122,7 @@ class Selection extends SelectionRecord { isAtEndOf(node) { const { endKey, endOffset } = this - const last = node.type == 'text' ? node : node.nodes.last() + const last = node.type == 'text' ? node : node.getLastTextNode() return endKey == last.key && endOffset == last.length } diff --git a/lib/models/state.js b/lib/models/state.js index 435ca7355..39d0ced1c 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -133,7 +133,11 @@ class State extends Record(DEFAULTS) { const { startKey } = selection const startNode = document.getNode(startKey) - if (selection.isExpanded) { + if (selection.isAtStartOf(document)) { + after = selection + } + + else if (selection.isExpanded) { after = selection.moveToStart() } diff --git a/lib/serializers/plaintext.js b/lib/serializers/plaintext.js index ef6c61ecb..881f31fc2 100644 --- a/lib/serializers/plaintext.js +++ b/lib/serializers/plaintext.js @@ -1,6 +1,6 @@ import Character from '../models/character' -import Node from '../models/node' +import Element from '../models/element' import Text from '../models/text' import Document from '../models/document' import State from '../models/state' @@ -33,13 +33,13 @@ function deserialize(string) { }, Character.createList()) const text = Text.create({ characters }) - const texts = Node.createMap([text]) - const node = Node.create({ + const texts = Element.createMap([text]) + const node = Element.create({ type: 'paragraph', nodes: texts, }) - const nodes = Node.createMap([node]) + const nodes = Element.createMap([node]) const document = Document.create({ nodes }) const state = State.create({ document }) return state diff --git a/lib/utils/group-by-marks.js b/lib/utils/group-by-marks.js index 424b91133..ee2f02d39 100644 --- a/lib/utils/group-by-marks.js +++ b/lib/utils/group-by-marks.js @@ -1,5 +1,5 @@ -import { List, Map, Record } from 'immutable' +import { List, Map, Record, Set } from 'immutable' /** * Range. @@ -7,7 +7,7 @@ import { List, Map, Record } from 'immutable' const Range = new Record({ text: '', - marks: new List() + marks: new Set() }) /** @@ -18,6 +18,12 @@ const Range = new Record({ */ function groupByMarks(characters) { + if (characters.size == 0) { + let ranges = new List() + ranges = ranges.push(new Range()) + return ranges + } + return characters .toList() .reduce((ranges, char, i) => { diff --git a/lib/utils/offset-key.js b/lib/utils/offset-key.js index 0a97c7c9a..9ffdc049c 100644 --- a/lib/utils/offset-key.js +++ b/lib/utils/offset-key.js @@ -10,6 +10,7 @@ const PARSER = /^(\w+)(?::(\d+)-(\d+))?$/ */ const ATTRIBUTE = 'data-offset-key' +const SELECTOR = `[${ATTRIBUTE}]` /** * From a `node`, find the closest parent's offset key. @@ -20,9 +21,17 @@ const ATTRIBUTE = 'data-offset-key' function findKey(node) { if (node.nodeType == 3) node = node.parentNode - const parent = node.closest(`[${ATTRIBUTE}]`) - if (!parent) return null - return parent.getAttribute(ATTRIBUTE) + + // If a parent with an offset key exists, use it. + const parent = node.closest(SELECTOR) + if (parent) return parent.getAttribute(ATTRIBUTE) + + // Otherwise, if a child with an offset key exists, use it. + const child = node.querySelector(SELECTOR) + if (child) return child.getAttribute(ATTRIBUTE) + + // Shouldn't get here... else we have an edge case to handle. + console.error('No offset key found for node:', node) } /**