mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-09-02 11:42:53 +02:00
broke lots of stuff, but added tests
This commit is contained in:
18
Makefile
18
Makefile
@@ -31,6 +31,10 @@ dist: $(shell find ./lib)
|
|||||||
example-auto-markdown:
|
example-auto-markdown:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/auto-markdown/build.js ./examples/auto-markdown/index.js
|
@ $(browserify) --debug --transform babelify --outfile ./examples/auto-markdown/build.js ./examples/auto-markdown/index.js
|
||||||
|
|
||||||
|
# Build the links example.
|
||||||
|
example-links:
|
||||||
|
@ $(browserify) --debug --transform babelify --outfile ./examples/links/build.js ./examples/links/index.js
|
||||||
|
|
||||||
# Build the plain-text example.
|
# Build the plain-text example.
|
||||||
example-plain-text:
|
example-plain-text:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js
|
@ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js
|
||||||
@@ -52,24 +56,28 @@ lint:
|
|||||||
@ $(standard) ./lib
|
@ $(standard) ./lib
|
||||||
|
|
||||||
# Build the test source.
|
# Build the test source.
|
||||||
test/support/build.js: $(shell find ./lib) ./test/browser.js
|
test/browser/support/build.js: $(shell find ./lib) ./test/browser/index.js
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./test/support/build.js ./test/browser.js
|
@ $(browserify) --debug --transform babelify --outfile ./test/browser/support/build.js ./test/browser/index.js
|
||||||
|
|
||||||
# Run the tests.
|
# Run the tests.
|
||||||
test: test-browser test-server
|
test: test-browser test-server
|
||||||
|
|
||||||
# Run the browser-side tests.
|
# Run the browser-side tests.
|
||||||
test-browser: ./test/support/build.js
|
test-browser: ./test/browser/support/build.js
|
||||||
@ $(mocha-phantomjs) --reporter spec --timeout 5000 ./test/support/browser.html
|
@ $(mocha-phantomjs) --reporter spec --timeout 5000 ./test/browser/support/browser.html
|
||||||
|
|
||||||
# Run the server-side tests.
|
# Run the server-side tests.
|
||||||
test-server:
|
test-server:
|
||||||
@ $(mocha) --reporter spec --timeout 5000 ./test/server.js
|
@ $(mocha) --compilers js:babel-core/register --reporter spec --timeout 5000 ./test/server
|
||||||
|
|
||||||
# Watch the auto-markdown example.
|
# Watch the auto-markdown example.
|
||||||
watch-example-auto-markdown:
|
watch-example-auto-markdown:
|
||||||
@ $(MAKE) example-auto-markdown browserify=$(watchify)
|
@ $(MAKE) example-auto-markdown browserify=$(watchify)
|
||||||
|
|
||||||
|
# Watch the links example.
|
||||||
|
watch-example-links:
|
||||||
|
@ $(MAKE) example-links browserify=$(watchify)
|
||||||
|
|
||||||
# Watch the plain-text example.
|
# Watch the plain-text example.
|
||||||
watch-example-plain-text:
|
watch-example-plain-text:
|
||||||
@ $(MAKE) example-plain-text browserify=$(watchify)
|
@ $(MAKE) example-plain-text browserify=$(watchify)
|
||||||
|
@@ -24,6 +24,7 @@ class App extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Render the example.
|
* Render the example.
|
||||||
*
|
*
|
||||||
* @return {Component} component
|
* @return {Component} component
|
||||||
@@ -153,12 +154,10 @@ class App extends React.Component {
|
|||||||
case '*':
|
case '*':
|
||||||
case '-':
|
case '-':
|
||||||
case '+':
|
case '+':
|
||||||
if (node.type == 'list-item') break
|
if (node.type == 'list-item') return
|
||||||
transform = node.type == 'list-item'
|
transform = transform
|
||||||
? transform
|
.setType('list-item')
|
||||||
: transform
|
.wrapBlock('bulleted-list')
|
||||||
.setType('list-item')
|
|
||||||
.wrap('bulleted-list')
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
@@ -196,7 +195,7 @@ class App extends React.Component {
|
|||||||
.transform()
|
.transform()
|
||||||
.setType('paragraph')
|
.setType('paragraph')
|
||||||
|
|
||||||
if (node.type == 'list-item') transform = transform.unwrap('bulleted-list')
|
if (node.type == 'list-item') transform = transform.unwrapBlock('bulleted-list')
|
||||||
|
|
||||||
state = transform.apply()
|
state = transform.apply()
|
||||||
return state
|
return state
|
||||||
|
48
examples/links/index.css
Normal file
48
examples/links/index.css
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
html {
|
||||||
|
background: #eee;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 40em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor > * > * + * {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
margin: 0 -10px;
|
||||||
|
padding: 1px 0 9px 8px;
|
||||||
|
border-bottom: 2px solid #eee;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu > * {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu > * + * {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
color: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button[data-active="true"] {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.material-icons {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
12
examples/links/index.html
Normal file
12
examples/links/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Editor | Links Example</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="index.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main></main>
|
||||||
|
<script src="build.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
159
examples/links/index.js
Normal file
159
examples/links/index.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
import Editor, { Mark, Raw } from '../..'
|
||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import state from './state.json'
|
||||||
|
import { Map } from 'immutable'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
state: Raw.deserialize(state)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the current selection has a link in it.
|
||||||
|
*
|
||||||
|
* @return {Boolean} hasLinks
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasLinks() {
|
||||||
|
let { state } = this.state
|
||||||
|
const { currentInlineNodes } = state
|
||||||
|
const hasLinks = currentInlineNodes.some(inline => inline.type == 'link')
|
||||||
|
return hasLinks
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When clicking a link, if the selection has a link in it, remove the link.
|
||||||
|
* Otherwise, add a new link with an href and text.
|
||||||
|
*
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
|
||||||
|
onClickLink(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
let { state } = this.state
|
||||||
|
const hasLinks = this.hasLinks()
|
||||||
|
|
||||||
|
if (hasLinks) {
|
||||||
|
state = state
|
||||||
|
.transform()
|
||||||
|
.unwrapInline('link')
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (state.isCurrentlyExpanded) {
|
||||||
|
// const href = window.prompt('Enter the URL of the link:')
|
||||||
|
state = state
|
||||||
|
.transform()
|
||||||
|
.wrapInline('link', new Map({ href: 'https://google.com' }))
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
const href = window.prompt('Enter the URL of the link:')
|
||||||
|
const text = window.prompt('Enter the text for the link:')
|
||||||
|
state = state
|
||||||
|
.transform()
|
||||||
|
.insertText(text)
|
||||||
|
.extendBackward(text.length)
|
||||||
|
.wrapInline('link', new Map({ href }))
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ state })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the app.
|
||||||
|
*
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.renderToolbar()}
|
||||||
|
{this.renderEditor()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the toolbar.
|
||||||
|
*
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderToolbar() {
|
||||||
|
const hasLinks = this.hasLinks()
|
||||||
|
return (
|
||||||
|
<div className="menu">
|
||||||
|
<span className="button" onMouseDown={e => this.onClickLink(e)} data-active={hasLinks}>
|
||||||
|
<span className="material-icons">link</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the editor.
|
||||||
|
*
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderEditor() {
|
||||||
|
return (
|
||||||
|
<div className="editor">
|
||||||
|
<Editor
|
||||||
|
state={this.state.state}
|
||||||
|
renderNode={node => this.renderNode(node)}
|
||||||
|
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 })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render our custom `node`.
|
||||||
|
*
|
||||||
|
* @param {Node} node
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
|
renderNode(node) {
|
||||||
|
switch (node.type) {
|
||||||
|
case 'link': {
|
||||||
|
return (props) => {
|
||||||
|
const { data } = props.node
|
||||||
|
const href = data.get('href')
|
||||||
|
return <a href={href}>{props.children}</a>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 'paragraph': {
|
||||||
|
return (props) => <p>{props.children}</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attach.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const app = <App />
|
||||||
|
const root = document.body.querySelector('main')
|
||||||
|
ReactDOM.render(app, root)
|
57
examples/links/state.json
Normal file
57
examples/links/state.json
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "In addition to block nodes, you can create inline nodes, like "
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "inline",
|
||||||
|
"type": "link",
|
||||||
|
"data": {
|
||||||
|
"href": "https://en.wikipedia.org/wiki/Hypertext"
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "hyperlinks"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "!"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "block",
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"kind": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "This example shows hyperlinks in action. It features two ways to add links. You can either add a link via the toolbar icon above, or if you want in on a little secret, copy a URL to your keyboard and paste it while a range of text is selected."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Editor | Auto-markdown Example</title>
|
<title>Editor | Table Example</title>
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
<link rel="stylesheet" href="index.css">
|
<link rel="stylesheet" href="index.css">
|
||||||
</head>
|
</head>
|
||||||
|
@@ -83,7 +83,7 @@ class Content extends React.Component {
|
|||||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
||||||
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
|
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
|
||||||
const focus = OffsetKey.findPoint(focusNode, focusOffset)
|
const focus = OffsetKey.findPoint(focusNode, focusOffset)
|
||||||
const edges = document.filterNodes((node) => {
|
const edges = document.filterDeep((node) => {
|
||||||
return node.key == anchor.key || node.key == focus.key
|
return node.key == anchor.key || node.key == focus.key
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -12,6 +12,7 @@ export default Editor
|
|||||||
|
|
||||||
export { default as Block } from './models/block'
|
export { default as Block } from './models/block'
|
||||||
export { default as Character } from './models/character'
|
export { default as Character } from './models/character'
|
||||||
|
export { default as Data } from './models/data'
|
||||||
export { default as Document } from './models/document'
|
export { default as Document } from './models/document'
|
||||||
export { default as Inline } from './models/inline'
|
export { default as Inline } from './models/inline'
|
||||||
export { default as Mark } from './models/mark'
|
export { default as Mark } from './models/mark'
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,16 @@ class Selection extends SelectionRecord {
|
|||||||
return this.isExpanded
|
return this.isExpanded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether the range's anchor of focus keys are not set yet.
|
||||||
|
*
|
||||||
|
* @return {Boolean} isUnset
|
||||||
|
*/
|
||||||
|
|
||||||
|
get isUnset() {
|
||||||
|
return this.anchorKey == null || this.focusKey == null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether the selection is forward.
|
* Get whether the selection is forward.
|
||||||
*
|
*
|
||||||
@@ -109,7 +119,7 @@ class Selection extends SelectionRecord {
|
|||||||
|
|
||||||
isAtStartOf(node) {
|
isAtStartOf(node) {
|
||||||
const { startKey, startOffset } = this
|
const { startKey, startOffset } = this
|
||||||
const first = node.kind == 'text' ? node : node.getFirstTextNode()
|
const first = node.kind == 'text' ? node : node.getFirstText()
|
||||||
return startKey == first.key && startOffset == 0
|
return startKey == first.key && startOffset == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +132,7 @@ class Selection extends SelectionRecord {
|
|||||||
|
|
||||||
isAtEndOf(node) {
|
isAtEndOf(node) {
|
||||||
const { endKey, endOffset } = this
|
const { endKey, endOffset } = this
|
||||||
const last = node.kind == 'text' ? node : node.getLastTextNode()
|
const last = node.kind == 'text' ? node : node.getLastText()
|
||||||
return endKey == last.key && endOffset == last.length
|
return endKey == last.key && endOffset == last.length
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,25 +152,25 @@ class Selection extends SelectionRecord {
|
|||||||
if (anchorKey == null || focusKey == null) return selection
|
if (anchorKey == null || focusKey == null) return selection
|
||||||
|
|
||||||
// Asset that the anchor and focus nodes exist in the node tree.
|
// Asset that the anchor and focus nodes exist in the node tree.
|
||||||
node.assertHasNode(anchorKey)
|
node.assertHasDeep(anchorKey)
|
||||||
node.assertHasNode(focusKey)
|
node.assertHasDeep(focusKey)
|
||||||
let anchorNode = node.getNode(anchorKey)
|
let anchorNode = node.getDeep(anchorKey)
|
||||||
let focusNode = node.getNode(focusKey)
|
let focusNode = node.getDeep(focusKey)
|
||||||
|
|
||||||
// If the anchor node isn't a text node, match it to one.
|
// If the anchor node isn't a text node, match it to one.
|
||||||
if (anchorNode.kind != 'text') {
|
if (anchorNode.kind != 'text') {
|
||||||
anchorNode = node.getTextNodeAtOffset(anchorOffset)
|
anchorNode = node.getTextAtOffset(anchorOffset)
|
||||||
let parent = node.getParentNode(anchorNode)
|
let parent = node.getParent(anchorNode)
|
||||||
let offset = parent.getNodeOffset(anchorNode)
|
let offset = parent.getOffset(anchorNode)
|
||||||
anchorOffset = anchorOffset - offset
|
anchorOffset = anchorOffset - offset
|
||||||
anchorKey = anchorNode.key
|
anchorKey = anchorNode.key
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the focus node isn't a text node, match it to one.
|
// If the focus node isn't a text node, match it to one.
|
||||||
if (focusNode.kind != 'text') {
|
if (focusNode.kind != 'text') {
|
||||||
focusNode = node.getTextNodeAtOffset(focusOffset)
|
focusNode = node.getTextAtOffset(focusOffset)
|
||||||
let parent = node.getParentNode(focusNode)
|
let parent = node.getParent(focusNode)
|
||||||
let offset = parent.getNodeOffset(focusNode)
|
let offset = parent.getOffset(focusNode)
|
||||||
focusOffset = focusOffset - offset
|
focusOffset = focusOffset - offset
|
||||||
focusKey = focusNode.key
|
focusKey = focusNode.key
|
||||||
}
|
}
|
||||||
|
@@ -32,8 +32,16 @@ const NODE_LIKE_METHODS = [
|
|||||||
'deleteAtRange',
|
'deleteAtRange',
|
||||||
'deleteBackwardAtRange',
|
'deleteBackwardAtRange',
|
||||||
'deleteForwardAtRange',
|
'deleteForwardAtRange',
|
||||||
'insertAtRange',
|
'insertTextAtRange',
|
||||||
'splitAtRange'
|
'markAtRange',
|
||||||
|
'setBlockAtRange',
|
||||||
|
'setInlineAtRange',
|
||||||
|
'splitBlockAtRange',
|
||||||
|
'splitInlineAtRange',
|
||||||
|
'unmarkAtRange',
|
||||||
|
'unwrapBlockAtRange',
|
||||||
|
'wrapBlockAtRange',
|
||||||
|
'wrapInlineAtRange'
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,7 +148,17 @@ class State extends Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get currentBlockNodes() {
|
get currentBlockNodes() {
|
||||||
return this.document.getBlockNodesAtRange(this.selection)
|
return this.document.getBlocksAtRange(this.selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the inline nodes in the current selection.
|
||||||
|
*
|
||||||
|
* @return {OrderedMap} nodes
|
||||||
|
*/
|
||||||
|
|
||||||
|
get currentInlineNodes() {
|
||||||
|
return this.document.getInlinesAtRange(this.selection)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -198,7 +216,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
// Determine what the selection should be after deleting.
|
// Determine what the selection should be after deleting.
|
||||||
const { startKey } = selection
|
const { startKey } = selection
|
||||||
const startNode = document.getNode(startKey)
|
const startNode = document.getDeep(startKey)
|
||||||
|
|
||||||
if (selection.isExpanded) {
|
if (selection.isExpanded) {
|
||||||
after = selection.moveToStart()
|
after = selection.moveToStart()
|
||||||
@@ -209,8 +227,8 @@ class State extends Record(DEFAULTS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (selection.isAtStartOf(startNode)) {
|
else if (selection.isAtStartOf(startNode)) {
|
||||||
const parent = document.getParentNode(startNode)
|
const parent = document.getParent(startNode)
|
||||||
const previous = document.getPreviousNode(parent).nodes.first()
|
const previous = document.getPrevious(parent).nodes.first()
|
||||||
after = selection.moveToEndOf(previous)
|
after = selection.moveToEndOf(previous)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,16 +301,31 @@ class State extends Record(DEFAULTS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the nodes in the current selection to `type`.
|
* Set the block nodes in the current selection to `type`.
|
||||||
*
|
*
|
||||||
* @param {String} type
|
* @param {String} type
|
||||||
* @return {State} state
|
* @return {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
setType(type) {
|
setBlock(type, data) {
|
||||||
let state = this
|
let state = this
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
document = document.setTypeAtRange(selection, type)
|
document = document.setBlockAtRange(selection, type, data)
|
||||||
|
state = state.merge({ document })
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the inline nodes in the current selection to `type`.
|
||||||
|
*
|
||||||
|
* @param {String} type
|
||||||
|
* @return {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
setInline(type, data) {
|
||||||
|
let state = this
|
||||||
|
let { document, selection } = state
|
||||||
|
document = document.setInlineAtRange(selection, type, data)
|
||||||
state = state.merge({ document })
|
state = state.merge({ document })
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@@ -313,9 +346,9 @@ class State extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
// Determine what the selection should be after splitting.
|
// Determine what the selection should be after splitting.
|
||||||
const { startKey } = selection
|
const { startKey } = selection
|
||||||
const startNode = document.getNode(startKey)
|
const startNode = document.getDeep(startKey)
|
||||||
const parent = document.getParentNode(startNode)
|
const parent = document.getParent(startNode)
|
||||||
const next = document.getNextNode(parent)
|
const next = document.getNext(parent)
|
||||||
const text = next.nodes.first()
|
const text = next.nodes.first()
|
||||||
selection = selection.moveToStartOf(text)
|
selection = selection.moveToStartOf(text)
|
||||||
|
|
||||||
@@ -345,10 +378,10 @@ class State extends Record(DEFAULTS) {
|
|||||||
* @return {State} state
|
* @return {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
wrap(type) {
|
wrapBlock(type) {
|
||||||
let state = this
|
let state = this
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
document = document.wrapAtRange(selection, type)
|
document = document.wrapBlockAtRange(selection, type)
|
||||||
state = state.merge({ document })
|
state = state.merge({ document })
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@@ -360,11 +393,43 @@ class State extends Record(DEFAULTS) {
|
|||||||
* @return {State} state
|
* @return {State} state
|
||||||
*/
|
*/
|
||||||
|
|
||||||
unwrap(type) {
|
unwrapBlock(type) {
|
||||||
let state = this
|
let state = this
|
||||||
let { document, selection } = state
|
let { document, selection } = state
|
||||||
selection = selection.normalize(document)
|
selection = selection.normalize(document)
|
||||||
document = document.unwrapAtRange(selection, type)
|
document = document.unwrapBlockAtRange(selection, type)
|
||||||
|
state = state.merge({ document, selection })
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the current selection in new inline nodes of `type`.
|
||||||
|
*
|
||||||
|
* @param {String} type
|
||||||
|
* @param {Map} data
|
||||||
|
* @return {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
wrapInline(type, data) {
|
||||||
|
let state = this
|
||||||
|
let { document, selection } = state
|
||||||
|
document = document.wrapInlineAtRange(selection, type, data)
|
||||||
|
state = state.merge({ document })
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unwrap the current selection from a parent of `type`.
|
||||||
|
*
|
||||||
|
* @param {String} type
|
||||||
|
* @return {State} state
|
||||||
|
*/
|
||||||
|
|
||||||
|
unwrapInline(type) {
|
||||||
|
let state = this
|
||||||
|
let { document, selection } = state
|
||||||
|
selection = selection.normalize(document)
|
||||||
|
document = document.unwrapInlineAtRange(selection, type)
|
||||||
state = state.merge({ document, selection })
|
state = state.merge({ document, selection })
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
@@ -46,16 +46,24 @@ const TRANSFORM_TYPES = [
|
|||||||
'insertTextAtRange',
|
'insertTextAtRange',
|
||||||
'mark',
|
'mark',
|
||||||
'markAtRange',
|
'markAtRange',
|
||||||
'setType',
|
'setBlock',
|
||||||
'setTypeAtRange',
|
'setBlockAtRange',
|
||||||
'split',
|
'setInline',
|
||||||
'splitAtRange',
|
'setInlineAtRange',
|
||||||
|
'splitBlock',
|
||||||
|
'splitBlockAtRange',
|
||||||
|
'splitInline',
|
||||||
|
'splitInlineAtRange',
|
||||||
'unmark',
|
'unmark',
|
||||||
'unmarkAtRange',
|
'unmarkAtRange',
|
||||||
'unwrap',
|
'unwrapBlock',
|
||||||
'unwrapAtRange',
|
'unwrapBlockAtRange',
|
||||||
'wrap',
|
'unwrapInline',
|
||||||
'wrapAtRange'
|
'unwrapInlineAtRange',
|
||||||
|
'wrapBlock',
|
||||||
|
'wrapBlockAtRange',
|
||||||
|
'wrapInline',
|
||||||
|
'wrapInlineAtRange'
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import keycode from 'keycode'
|
import keycode from 'keycode'
|
||||||
import { IS_WINDOWS, IS_MAC } from '../utils/environment'
|
import environment from '../utils/environment'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
@@ -20,6 +20,7 @@ export default {
|
|||||||
|
|
||||||
onKeyDown(e, state, editor) {
|
onKeyDown(e, state, editor) {
|
||||||
const key = keycode(e.which)
|
const key = keycode(e.which)
|
||||||
|
const { IS_WINDOWS, IS_MAC } = environment()
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'enter': {
|
case 'enter': {
|
||||||
|
@@ -17,9 +17,7 @@ import { Map } from 'immutable'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function serialize(state) {
|
function serialize(state) {
|
||||||
return {
|
return serializeNode(state.document)
|
||||||
nodes: serializeNode(state.document)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,12 +42,12 @@ function serializeNode(node) {
|
|||||||
}
|
}
|
||||||
case 'block':
|
case 'block':
|
||||||
case 'inline': {
|
case 'inline': {
|
||||||
return {
|
const obj = {}
|
||||||
data: node.data.toJSON(),
|
obj.kind = node.kind
|
||||||
kind: node.kind,
|
obj.type = node.type
|
||||||
nodes: node.nodes.toArray().map(node => serializeNode(node)),
|
obj.nodes = node.nodes.toArray().map(node => serializeNode(node))
|
||||||
type: node.type
|
if (node.data.size) obj.data = node.data.toJSON()
|
||||||
}
|
return obj
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,10 +63,10 @@ function serializeCharacters(characters) {
|
|||||||
return groupByMarks(characters)
|
return groupByMarks(characters)
|
||||||
.toArray()
|
.toArray()
|
||||||
.map((range) => {
|
.map((range) => {
|
||||||
return {
|
const obj = {}
|
||||||
text: range.text,
|
obj.text = range.text
|
||||||
marks: range.marks.map(serializeMark)
|
if (range.marks.size) obj.marks = range.marks.toArray().map(serializeMark)
|
||||||
}
|
return obj
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +78,10 @@ function serializeCharacters(characters) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function serializeMark(mark) {
|
function serializeMark(mark) {
|
||||||
return {
|
const obj = {}
|
||||||
type: mark.type,
|
obj.type = mark.type
|
||||||
data: mark.data.toJSON()
|
if (mark.data.size) obj.data = mark.data.toJSON()
|
||||||
}
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -3,16 +3,28 @@ import browser from 'detect-browser'
|
|||||||
import Parser from 'ua-parser-js'
|
import Parser from 'ua-parser-js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detections.
|
* Read the environment.
|
||||||
|
*
|
||||||
|
* @return {Object} environment
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const IS_ANDROID = browser.name === 'android'
|
function environment() {
|
||||||
export const IS_CHROME = browser.name === 'chrome'
|
return {
|
||||||
export const IS_EDGE = browser.name === 'edge'
|
IS_ANDROID: browser.name === 'android',
|
||||||
export const IS_FIREFOX = browser.name === 'firefox'
|
IS_CHROME: browser.name === 'chrome',
|
||||||
export const IS_IE = browser.name === 'ie'
|
IS_EDGE: browser.name === 'edge',
|
||||||
export const IS_IOS = browser.name === 'ios'
|
IS_FIREFOX: browser.name === 'firefox',
|
||||||
export const IS_MAC = new Parser().getOS().name === 'Mac OS'
|
IS_IE: browser.name === 'ie',
|
||||||
export const IS_UBUNTU = new Parser().getOS().name === 'Ubuntu'
|
IS_IOS: browser.name === 'ios',
|
||||||
export const IS_SAFARI = browser.name === 'safari'
|
IS_MAC: new Parser().getOS().name === 'Mac OS',
|
||||||
export const IS_WINDOWS = new Parser().getOS().name.includes('Windows')
|
IS_UBUNTU: new Parser().getOS().name === 'Ubuntu',
|
||||||
|
IS_SAFARI: browser.name === 'safari',
|
||||||
|
IS_WINDOWS: new Parser().getOS().name.includes('Windows')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default environment
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
"keycode": "^2.1.2",
|
"keycode": "^2.1.2",
|
||||||
"lodash": "^4.13.1",
|
"lodash": "^4.13.1",
|
||||||
"react": "^15.1.0",
|
"react": "^15.1.0",
|
||||||
"to-camel-case": "^1.0.0",
|
|
||||||
"ua-parser-js": "^0.7.10",
|
"ua-parser-js": "^0.7.10",
|
||||||
"uid": "0.0.2"
|
"uid": "0.0.2"
|
||||||
},
|
},
|
||||||
@@ -21,10 +20,13 @@
|
|||||||
"babel-preset-stage-0": "^6.5.0",
|
"babel-preset-stage-0": "^6.5.0",
|
||||||
"babelify": "^7.3.0",
|
"babelify": "^7.3.0",
|
||||||
"browserify": "^13.0.1",
|
"browserify": "^13.0.1",
|
||||||
|
"component-type": "^1.2.1",
|
||||||
"mocha": "^2.5.3",
|
"mocha": "^2.5.3",
|
||||||
"mocha-phantomjs": "^4.0.2",
|
"mocha-phantomjs": "^4.0.2",
|
||||||
"react-dom": "^15.1.0",
|
"react-dom": "^15.1.0",
|
||||||
|
"read-metadata": "^1.0.0",
|
||||||
"standard": "^7.1.2",
|
"standard": "^7.1.2",
|
||||||
|
"to-camel-case": "^1.0.0",
|
||||||
"watchify": "^3.7.0"
|
"watchify": "^3.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
185
test/helpers/assert-json.js
Normal file
185
test/helpers/assert-json.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
|
||||||
|
import assert from 'assert'
|
||||||
|
import type from 'component-type'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assertion error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const AssertionError = assert.AssertionError
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that an `actual` JSON object equals an `expected` value.
|
||||||
|
*
|
||||||
|
* @param {Object} actual
|
||||||
|
* @param {Object} expected
|
||||||
|
* @throws {AssertionError}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function equal(actual, expected, message) {
|
||||||
|
if (!test(actual, expected)) {
|
||||||
|
throw new AssertionError({
|
||||||
|
actual: actual,
|
||||||
|
expected: wrap(actual, expected),
|
||||||
|
operator: '==',
|
||||||
|
stackStartFunction: equal
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that an `actual` JSON object does not equal an `expected` value.
|
||||||
|
*
|
||||||
|
* @param {Object} actual
|
||||||
|
* @param {Object} expected
|
||||||
|
* @throws {AssertionError}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function notEqual(actual, expected, message) {
|
||||||
|
if (test(actual, expected)) {
|
||||||
|
throw new AssertionError({
|
||||||
|
actual: actual,
|
||||||
|
expected: wrap(actual, expected),
|
||||||
|
operator: '!=',
|
||||||
|
stackStartFunction: notEqual
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that an `actual` JSON object strict equals an `expected` value.
|
||||||
|
*
|
||||||
|
* @param {Object} actual
|
||||||
|
* @param {Object} expected
|
||||||
|
* @throws {AssertionError}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function strictEqual(actual, expected, message) {
|
||||||
|
if (!test(actual, expected, true)) {
|
||||||
|
throw new AssertionError({
|
||||||
|
actual: actual,
|
||||||
|
expected: wrap(actual, expected),
|
||||||
|
operator: '===',
|
||||||
|
stackStartFunction: equal
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that an `actual` JSON object does not strict equal an `expected` value.
|
||||||
|
*
|
||||||
|
* @param {Object} actual
|
||||||
|
* @param {Object} expected
|
||||||
|
* @throws {AssertionError}
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function notStrictEqual(actual, expected, message) {
|
||||||
|
if (test(actual, expected, true)) {
|
||||||
|
throw new AssertionError({
|
||||||
|
actual: actual,
|
||||||
|
expected: wrap(actual, expected),
|
||||||
|
operator: '!==',
|
||||||
|
stackStartFunction: notEqual
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an `actual` JSON value equals an `expected` JSON value.
|
||||||
|
*
|
||||||
|
* If a function is passed as any value, it is called with the actual value and
|
||||||
|
* must return a boolean.
|
||||||
|
*
|
||||||
|
* Strict mode uses strict equality, forces arrays to be of the same length, and
|
||||||
|
* objects to have the same keys.
|
||||||
|
*
|
||||||
|
* @param {Mixed} actual
|
||||||
|
* @param {Mixed} expected
|
||||||
|
* @param {Boolean} strict
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function test(actual, expected, strict) {
|
||||||
|
if (type(expected) == 'function') return !! expected(actual)
|
||||||
|
if (type(actual) != type(expected)) return false
|
||||||
|
|
||||||
|
switch (type(expected)) {
|
||||||
|
case 'object':
|
||||||
|
return object(actual, expected, strict)
|
||||||
|
case 'array':
|
||||||
|
return array(actual, expected, strict)
|
||||||
|
default:
|
||||||
|
return strict ? actual === expected : actual == expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an `actual` object equals an `expected` object.
|
||||||
|
*
|
||||||
|
* @param {Object} object
|
||||||
|
* @param {Object} expected
|
||||||
|
* @param {Boolean} strict
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function object(actual, expected, strict) {
|
||||||
|
if (strict) {
|
||||||
|
var ka = Object.keys(actual).sort()
|
||||||
|
var ke = Object.keys(expected).sort()
|
||||||
|
if (!test(ka, ke, strict)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in expected) {
|
||||||
|
if (!test(actual[key], expected[key], strict)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that an `actual` array equals an `expected` array.
|
||||||
|
*
|
||||||
|
* @param {Array} actual
|
||||||
|
* @param {Array} expected
|
||||||
|
* @param {Boolean} strict
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function array(actual, expected, strict) {
|
||||||
|
if (strict) {
|
||||||
|
if (!test(actual.length, expected.length, strict)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
if (!test(actual[i], expected[i], strict)) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap an expected value to remove annoying false negatives.
|
||||||
|
*
|
||||||
|
* @param {Mixed} actual
|
||||||
|
* @param {Mixed} expected
|
||||||
|
* @return {Mixed}
|
||||||
|
*/
|
||||||
|
|
||||||
|
function wrap(actual, expected) {
|
||||||
|
if (type(expected) == 'function') return expected(actual) ? actual : expected
|
||||||
|
if (type(actual) != type(expected)) return expected
|
||||||
|
|
||||||
|
if (type(expected) == 'object') {
|
||||||
|
for (var key in expected) {
|
||||||
|
expected[key] = wrap(actual[key], expected[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type(expected) == 'array') {
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
expected[i] = wrap(actual[i], expected[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expected
|
||||||
|
}
|
@@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const Editor = require('..')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests.
|
|
||||||
*/
|
|
||||||
|
|
||||||
describe('server', () => {
|
|
||||||
|
|
||||||
})
|
|
2
test/server/index.js
Normal file
2
test/server/index.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
import './transforms'
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: ord
|
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const second = texts.last()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 2,
|
||||||
|
focusKey: second.key,
|
||||||
|
focusOffset: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: another
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: woother
|
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const second = texts.last()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: second.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: another
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wordanother
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length - 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wor
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wrd
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: ""
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteBackwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: ord
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const second = texts.last()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: second.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: second.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteBackwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: another
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wordanother
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteBackwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wor
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 2,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteBackwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wrd
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteBackwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteForwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteForwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: ord
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteForwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: another
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wordanother
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length - 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length - 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteForwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wor
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.deleteForwardAtRange(range)
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wrd
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 3,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 3
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: or
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: ora
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: or
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wa
|
||||||
|
- text: or
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 2,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 2
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: or
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: oar
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: d
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: aword
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, ' ')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: " word"
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 0,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a few words ')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: a few words word
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: worda
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, ' ')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: "word "
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: first.length,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: first.length
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, ' a few words')
|
||||||
|
.apply()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word a few words
|
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
export default function (state) {
|
||||||
|
const { document, selection } = state
|
||||||
|
const texts = document.getTextNodes()
|
||||||
|
const first = texts.first()
|
||||||
|
const range = selection.merge({
|
||||||
|
anchorKey: first.key,
|
||||||
|
anchorOffset: 1,
|
||||||
|
focusKey: first.key,
|
||||||
|
focusOffset: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
.transform()
|
||||||
|
.insertTextAtRange(range, 'a')
|
||||||
|
.apply()
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user