mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-28 09:29:49 +02:00
wow big refactor, added raw serializer
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
|
||||
import Editor, { State } from '../..'
|
||||
import Editor, { State, Raw } from '../..'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
|
||||
@@ -33,7 +33,11 @@ const state = {
|
||||
},
|
||||
{
|
||||
text: 'simple',
|
||||
marks: ['bold']
|
||||
marks: [
|
||||
{
|
||||
type: 'bold'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
text: ' paragraph of text.'
|
||||
@@ -78,14 +82,14 @@ function renderNode(node) {
|
||||
}
|
||||
|
||||
function renderMark(mark) {
|
||||
switch (mark) {
|
||||
switch (mark.type) {
|
||||
case 'bold': {
|
||||
return {
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown mark type "${mark}".`)
|
||||
throw new Error(`Unknown mark type "${mark.type}".`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +101,7 @@ function renderMark(mark) {
|
||||
class App extends React.Component {
|
||||
|
||||
state = {
|
||||
state: State.create(state)
|
||||
state: Raw.deserialize(state)
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@@ -1,7 +1,8 @@
|
||||
|
||||
import Content from './content'
|
||||
import React from 'react'
|
||||
import CORE_PLUGIN from '../plugins/core'
|
||||
import State from '../models/state'
|
||||
import corePlugin from '../plugins/core'
|
||||
|
||||
/**
|
||||
* Editor.
|
||||
@@ -19,20 +20,38 @@ class Editor extends React.Component {
|
||||
|
||||
static defaultProps = {
|
||||
plugins: [],
|
||||
state: {}
|
||||
state: new State()
|
||||
};
|
||||
|
||||
/**
|
||||
* When created, compute the plugins from `props`.
|
||||
*
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
this.state.plugins = this.resolvePlugins(props)
|
||||
}
|
||||
|
||||
/**
|
||||
* When the `props` are updated, recompute the plugins.
|
||||
*
|
||||
* @param {Object} props
|
||||
*/
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
const plugins = this.resolvePlugins(props)
|
||||
this.setState({ plugins })
|
||||
}
|
||||
|
||||
/**
|
||||
* When the `state` changes, pass through plugins, then bubble up.
|
||||
*
|
||||
* @param {State} state
|
||||
*/
|
||||
|
||||
onChange(state) {
|
||||
if (state == this.props.state) return
|
||||
|
||||
@@ -57,19 +76,20 @@ class Editor extends React.Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the `keydown` event.
|
||||
* When an event by `name` fires, pass it through the plugins, and update the
|
||||
* state if one of them chooses to.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Event} e
|
||||
*/
|
||||
|
||||
onKeyDown(e) {
|
||||
onEvent(name, e) {
|
||||
for (const plugin of this.state.plugins) {
|
||||
if (plugin.onKeyDown) {
|
||||
const newState = plugin.onKeyDown(e, this.props.state, this)
|
||||
if (newState == null) continue
|
||||
this.props.onChange(newState)
|
||||
break
|
||||
}
|
||||
if (!plugin[name]) continue
|
||||
const newState = plugin[name](e, this.props.state, this)
|
||||
if (!newState) continue
|
||||
this.props.onChange(newState)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,15 +106,31 @@ class Editor extends React.Component {
|
||||
renderNode={this.props.renderNode}
|
||||
state={this.props.state}
|
||||
onChange={state => this.onChange(state)}
|
||||
onKeyDown={e => this.onKeyDown(e)}
|
||||
onKeyDown={e => this.onEvent('keyDown', e)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the editor's current plugins from `props` when they change.
|
||||
*
|
||||
* Add a plugin made from the editor's own `props` at the beginning of the
|
||||
* stack. That way, you can add a `onKeyDown` handler to the editor itself,
|
||||
* and it will override all of the existing plugins.
|
||||
*
|
||||
* Also add the "core" functionality plugin that handles the most basic events
|
||||
* for the editor, like delete characters and such.
|
||||
*
|
||||
* @param {Object} props
|
||||
* @return {Array} plugins
|
||||
*/
|
||||
|
||||
resolvePlugins(props) {
|
||||
const { onChange, plugins, ...editorPlugin } = props
|
||||
return [
|
||||
...props.plugins,
|
||||
CORE_PLUGIN
|
||||
editorPlugin,
|
||||
...plugins,
|
||||
corePlugin
|
||||
]
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,6 @@
|
||||
import OffsetKey from '../utils/offset-key'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import createOffsetKey from '../utils/create-offset-key'
|
||||
|
||||
/**
|
||||
* Leaf.
|
||||
@@ -11,10 +10,13 @@ import createOffsetKey from '../utils/create-offset-key'
|
||||
class Leaf extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
marks: React.PropTypes.array.isRequired,
|
||||
node: React.PropTypes.object.isRequired,
|
||||
range: React.PropTypes.object.isRequired,
|
||||
start: React.PropTypes.number.isRequired,
|
||||
end: React.PropTypes.number.isRequired,
|
||||
renderMark: React.PropTypes.func.isRequired,
|
||||
state: React.PropTypes.object.isRequired,
|
||||
text: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@@ -33,11 +35,8 @@ class Leaf extends React.Component {
|
||||
if (!selection.isFocused) return
|
||||
|
||||
const { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
||||
const { node, range } = this.props
|
||||
const { node, start, end } = this.props
|
||||
const { key } = node
|
||||
const { offset, text } = range
|
||||
const start = offset
|
||||
const end = offset + text.length
|
||||
|
||||
// If neither matches, the selection doesn't start or end here, so exit.
|
||||
const hasStart = key == anchorKey && start <= anchorOffset && anchorOffset <= end
|
||||
@@ -95,13 +94,12 @@ class Leaf extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { node, range } = this.props
|
||||
const { text } = range
|
||||
const { node, start, end, text, marks } = this.props
|
||||
const styles = this.renderStyles()
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
key: node.key,
|
||||
start: range.offset,
|
||||
end: range.offset + range.text.length
|
||||
start,
|
||||
end
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -110,14 +108,13 @@ class Leaf extends React.Component {
|
||||
data-offset-key={offsetKey}
|
||||
data-type='leaf'
|
||||
>
|
||||
{text == '' ? <br/> : text}
|
||||
{text || <br/>}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
renderStyles() {
|
||||
const { range, renderMark } = this.props
|
||||
const { marks } = range
|
||||
const { marks, renderMark } = this.props
|
||||
return marks.reduce((styles, mark) => {
|
||||
return {
|
||||
...styles,
|
||||
|
@@ -1,8 +1,8 @@
|
||||
|
||||
import Leaf from './leaf'
|
||||
import OffsetKey from '../utils/offset-key'
|
||||
import Raw from '../serializers/raw'
|
||||
import React from 'react'
|
||||
import convertCharactersToRanges from '../utils/convert-characters-to-ranges'
|
||||
import createOffsetKey from '../utils/create-offset-key'
|
||||
|
||||
/**
|
||||
* Text.
|
||||
@@ -18,33 +18,47 @@ class Text extends React.Component {
|
||||
|
||||
render() {
|
||||
const { node } = this.props
|
||||
const { characters } = node
|
||||
const ranges = convertCharactersToRanges(characters)
|
||||
const leaves = ranges.length
|
||||
? ranges.map(range => this.renderLeaf(range))
|
||||
: this.renderSpacerLeaf()
|
||||
|
||||
return (
|
||||
<span
|
||||
key={node.key}
|
||||
data-key={node.key}
|
||||
data-type='text'
|
||||
>
|
||||
{leaves}
|
||||
{this.renderLeaves()}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
renderLeaves() {
|
||||
const { node } = this.props
|
||||
const { characters } = node
|
||||
const ranges = Raw.serializeCharacters(characters)
|
||||
return ranges.length
|
||||
? ranges.map((range) => this.renderLeaf(range))
|
||||
: this.renderSpacerLeaf()
|
||||
}
|
||||
|
||||
renderLeaf(range) {
|
||||
const { node, renderMark, state } = this.props
|
||||
const key = createOffsetKey(node, range)
|
||||
const { marks, offset, text } = range
|
||||
const start = offset
|
||||
const end = offset + text.length
|
||||
const offsetKey = OffsetKey.stringify({
|
||||
key: node.key,
|
||||
start,
|
||||
end
|
||||
})
|
||||
|
||||
return (
|
||||
<Leaf
|
||||
key={key}
|
||||
range={range}
|
||||
key={offsetKey}
|
||||
marks={marks}
|
||||
node={node}
|
||||
start={start}
|
||||
end={end}
|
||||
renderMark={renderMark}
|
||||
state={state}
|
||||
text={text}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@@ -15,6 +15,12 @@ import Selection from './models/selection'
|
||||
import State from './models/state'
|
||||
import Text from './models/text'
|
||||
|
||||
/**
|
||||
* Serializers.
|
||||
*/
|
||||
|
||||
import Raw from './serializers/raw'
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
@@ -23,6 +29,7 @@ export default Editor
|
||||
export {
|
||||
Character,
|
||||
Node,
|
||||
Raw,
|
||||
Selection,
|
||||
State,
|
||||
Text
|
||||
|
@@ -17,28 +17,25 @@ const CharacterRecord = new Record({
|
||||
class Character extends CharacterRecord {
|
||||
|
||||
/**
|
||||
* Create a character record from a Javascript `object`.
|
||||
* Create a character record with `properties`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @param {Object} properties
|
||||
* @return {Character} character
|
||||
*/
|
||||
|
||||
static create(object) {
|
||||
return new Character({
|
||||
text: object.text,
|
||||
marks: new List(object.marks)
|
||||
})
|
||||
static create(properties = {}) {
|
||||
return new Character(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of characters from a Javascript `array`.
|
||||
* Create a characters list from an array of characters.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @return {List} characters
|
||||
*/
|
||||
|
||||
static createList(array) {
|
||||
return new List(array.map(object => Character.create(object)))
|
||||
static createList(array = []) {
|
||||
return new List(array)
|
||||
}
|
||||
|
||||
}
|
||||
|
48
lib/models/mark.js
Normal file
48
lib/models/mark.js
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
import { List, Map, Record } from 'immutable'
|
||||
|
||||
/**
|
||||
* Record.
|
||||
*/
|
||||
|
||||
const MarkRecord = new Record({
|
||||
data: new Map(),
|
||||
type: null
|
||||
})
|
||||
|
||||
/**
|
||||
* Mark.
|
||||
*/
|
||||
|
||||
class Mark extends MarkRecord {
|
||||
|
||||
/**
|
||||
* Create a new `Mark` with `properties`.
|
||||
*
|
||||
* @param {Object} properties
|
||||
* @return {Mark} mark
|
||||
*/
|
||||
|
||||
static create(properties = {}) {
|
||||
if (!properties.type) throw new Error('You must provide a `type` for the mark.')
|
||||
return new Mark(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a marks list from an array of marks.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @return {List} marks
|
||||
*/
|
||||
|
||||
static createList(array = []) {
|
||||
return new List(array)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
||||
export default Mark
|
@@ -21,36 +21,30 @@ const NodeRecord = new Record({
|
||||
class Node extends NodeRecord {
|
||||
|
||||
/**
|
||||
* Create a node record from a Javascript `object`.
|
||||
* Create a new `Node` with `properties`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @param {Object} properties
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
static create(object) {
|
||||
return new Node({
|
||||
data: new Map(object.data || {}),
|
||||
key: uid(4),
|
||||
nodes: Node.createMap(object.nodes || []),
|
||||
type: object.type
|
||||
})
|
||||
static create(properties = {}) {
|
||||
if (!properties.type) throw new Error('You must pass a node `type`.')
|
||||
properties.key = uid(4)
|
||||
return new Node(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ordered map of nodes from a Javascript `array` of nodes.
|
||||
* Create an ordered map of `Nodes` from an array of `Nodes`.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @return {OrderedMap} nodes
|
||||
* @param {Array} nodes
|
||||
* @return {OrderedMap} map
|
||||
*/
|
||||
|
||||
static createMap(array) {
|
||||
return new OrderedMap(array.reduce((map, object) => {
|
||||
const node = object.type == 'text'
|
||||
? Text.create(object)
|
||||
: Node.create(object)
|
||||
map[node.key] = node
|
||||
static createMap(nodes = []) {
|
||||
return nodes.reduce((map, node) => {
|
||||
map = map.set(node.key, node)
|
||||
return map
|
||||
}, {}))
|
||||
}, new OrderedMap())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -21,13 +21,14 @@ const SelectionRecord = new Record({
|
||||
class Selection extends SelectionRecord {
|
||||
|
||||
/**
|
||||
* Create a new `Selection` from `attrs`.
|
||||
* Create a new `Selection` with `properties`.
|
||||
*
|
||||
* @param {Object} properties
|
||||
* @return {Selection} selection
|
||||
*/
|
||||
|
||||
static create(attrs) {
|
||||
return new Selection(attrs)
|
||||
static create(properties = {}) {
|
||||
return new Selection(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -60,17 +60,14 @@ const SELECTION_LIKE_METHODS = [
|
||||
class State extends StateRecord {
|
||||
|
||||
/**
|
||||
* Create a new `State` from a Javascript `object`.
|
||||
* Create a new `State` with `properties`.
|
||||
*
|
||||
* @param {Objetc} object
|
||||
* @param {Objetc} properties
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
static create(attrs) {
|
||||
return new State({
|
||||
nodes: Node.createMap(attrs.nodes),
|
||||
selection: Selection.create(attrs.selection)
|
||||
})
|
||||
static create(properties = {}) {
|
||||
return new State(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -19,18 +19,15 @@ const TextRecord = new Record({
|
||||
class Text extends TextRecord {
|
||||
|
||||
/**
|
||||
* Create a text record from a Javascript `object`.
|
||||
* Create a new `Text` with `properties`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @param {Object} properties
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
static create(attrs) {
|
||||
const characters = convertRangesToCharacters(attrs.ranges || [])
|
||||
return new Text({
|
||||
key: uid(4),
|
||||
characters
|
||||
})
|
||||
static create(properties = {}) {
|
||||
properties.key = uid(4)
|
||||
return new Text(properties)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -3,10 +3,10 @@ import keycode from 'keycode'
|
||||
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
|
||||
|
||||
/**
|
||||
* The core plugin.
|
||||
* Export.
|
||||
*/
|
||||
|
||||
const CORE_PLUGIN = {
|
||||
export default {
|
||||
|
||||
/**
|
||||
* The core `onKeyDown` handler.
|
||||
@@ -124,9 +124,3 @@ function isCommand(e) {
|
||||
? e.metaKey && !e.altKey
|
||||
: e.ctrlKey && !e.altKey
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
||||
export default CORE_PLUGIN
|
||||
|
179
lib/serializers/raw.js
Normal file
179
lib/serializers/raw.js
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
import Character from '../models/character'
|
||||
import Mark from '../models/mark'
|
||||
import Node from '../models/node'
|
||||
import Text from '../models/text'
|
||||
import State from '../models/state'
|
||||
import xor from 'lodash/xor'
|
||||
import { Map } from 'immutable'
|
||||
|
||||
/**
|
||||
* Serialize a `state`.
|
||||
*
|
||||
* @param {State} state
|
||||
* @return {Object} object
|
||||
*/
|
||||
|
||||
function serialize(state) {
|
||||
return {
|
||||
nodes: state.nodes.toArray().map(node => serializeNode(node))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a `node`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @return {Object} object
|
||||
*/
|
||||
|
||||
function serializeNode(node) {
|
||||
switch (node.type) {
|
||||
case 'text': {
|
||||
return {
|
||||
type: 'text',
|
||||
ranges: serializeCharacters(node.characters)
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
type: node.type,
|
||||
data: node.data.toJSON(),
|
||||
nodes: node.nodes.toArray().map(node => serializeNode(node))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a list of `characters`.
|
||||
*
|
||||
* @param {List} characters
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
function serializeCharacters(characters) {
|
||||
return characters
|
||||
.toArray()
|
||||
.reduce((ranges, char, i) => {
|
||||
const previous = i == 0 ? null : characters.get(i - 1)
|
||||
const { text } = char
|
||||
const marks = char.marks.toArray().map(mark => serializeMark(mark))
|
||||
|
||||
if (previous) {
|
||||
const previousMarks = previous.marks.toArray()
|
||||
const diff = xor(marks, previousMarks)
|
||||
if (!diff.length) {
|
||||
const previousRange = ranges[ranges.length - 1]
|
||||
previousRange.text += text
|
||||
return ranges
|
||||
}
|
||||
}
|
||||
|
||||
const offset = ranges.map(range => range.text).join('').length
|
||||
ranges.push({ text, marks, offset })
|
||||
return ranges
|
||||
}, [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a `mark`.
|
||||
*
|
||||
* @param {Mark} mark
|
||||
* @return {Object} Object
|
||||
*/
|
||||
|
||||
function serializeMark(mark) {
|
||||
return {
|
||||
type: mark.type,
|
||||
data: mark.data.toJSON()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a state JSON `object`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @return {State} state
|
||||
*/
|
||||
|
||||
function deserialize(object) {
|
||||
return State.create({
|
||||
nodes: Node.createMap(object.nodes.map(deserializeNode))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a node JSON `object`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @return {Node} node
|
||||
*/
|
||||
|
||||
function deserializeNode(object) {
|
||||
switch (object.type) {
|
||||
case 'text': {
|
||||
return Text.create({
|
||||
characters: deserializeRanges(object.ranges)
|
||||
})
|
||||
}
|
||||
default: {
|
||||
return Node.create({
|
||||
type: object.type,
|
||||
data: new Map(object.data),
|
||||
nodes: Node.createMap(object.nodes.map(deserializeNode))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a JSON `array` of ranges.
|
||||
*
|
||||
* @param {Array} array
|
||||
* @return {List} characters
|
||||
*/
|
||||
|
||||
function deserializeRanges(array) {
|
||||
return array.reduce((characters, object) => {
|
||||
const marks = object.marks || []
|
||||
const chars = object.text
|
||||
.split('')
|
||||
.map(char => {
|
||||
return Character.create({
|
||||
text: char,
|
||||
marks: Mark.createList(marks.map(deserializeMark))
|
||||
})
|
||||
})
|
||||
|
||||
return characters.push(...chars)
|
||||
}, Character.createList())
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a mark JSON `object`.
|
||||
*
|
||||
* @param {Object} object
|
||||
* @return {Mark} mark
|
||||
*/
|
||||
|
||||
function deserializeMark(object) {
|
||||
return Mark.create({
|
||||
type: object.type,
|
||||
data: new Map(object.data)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*/
|
||||
|
||||
export default {
|
||||
serialize,
|
||||
serializeCharacters,
|
||||
serializeMark,
|
||||
serializeNode,
|
||||
deserialize,
|
||||
deserializeNode,
|
||||
deserializeRanges
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
|
||||
import xor from 'lodash/xor'
|
||||
|
||||
/**
|
||||
* Convert a `characters` list to `ranges`.
|
||||
*
|
||||
* @param {CharacterList} characters
|
||||
* @return {Array} ranges
|
||||
*/
|
||||
|
||||
export default function convertCharactersToRanges(characters) {
|
||||
return characters
|
||||
.toArray()
|
||||
.reduce((ranges, char, i) => {
|
||||
const previous = i == 0 ? null : characters.get(i - 1)
|
||||
const { text } = char
|
||||
const marks = char.marks.toArray()
|
||||
|
||||
if (previous) {
|
||||
const previousMarks = previous.marks.toArray()
|
||||
const diff = xor(marks, previousMarks)
|
||||
if (!diff.length) {
|
||||
const previousRange = ranges[ranges.length - 1]
|
||||
previousRange.text += text
|
||||
return ranges
|
||||
}
|
||||
}
|
||||
|
||||
const offset = ranges.map(range => range.text).join('').length
|
||||
ranges.push({ text, marks, offset })
|
||||
return ranges
|
||||
}, [])
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
|
||||
import Character from '../models/character'
|
||||
|
||||
/**
|
||||
* Convert a `characters` list to `ranges`.
|
||||
*
|
||||
* @param {CharacterList} characters
|
||||
* @return {Array} ranges
|
||||
*/
|
||||
|
||||
export default function convertRangesToCharacters(ranges) {
|
||||
return Character.createList(ranges.reduce((characters, range) => {
|
||||
const chars = range.text
|
||||
.split('')
|
||||
.map(char => {
|
||||
return {
|
||||
text: char,
|
||||
marks: range.marks
|
||||
}
|
||||
})
|
||||
return characters.concat(chars)
|
||||
}, []))
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
|
||||
/**
|
||||
* Create an offset key from a `node` and a `range`.
|
||||
*
|
||||
* @param {Node} node
|
||||
* @param {Object} range
|
||||
* @property {Number} offset
|
||||
* @property {String} text
|
||||
* @return {String} offsetKey
|
||||
*/
|
||||
|
||||
export default function createOffsetKey(node, range) {
|
||||
const start = range.offset
|
||||
const end = range.offset + range.text.length
|
||||
return `${node.key}.${start}-${end}`
|
||||
}
|
Reference in New Issue
Block a user