mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-06 15:26:34 +02:00
refactor examples, normalize selections
This commit is contained in:
8
Makefile
8
Makefile
@@ -31,10 +31,6 @@ 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 basic example.
|
|
||||||
example-basic:
|
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/basic/build.js ./examples/basic/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
|
||||||
@@ -70,10 +66,6 @@ test-server:
|
|||||||
watch-example-auto-markdown:
|
watch-example-auto-markdown:
|
||||||
@ $(MAKE) example-auto-markdown browserify=$(watchify)
|
@ $(MAKE) example-auto-markdown browserify=$(watchify)
|
||||||
|
|
||||||
# Watch the basic example.
|
|
||||||
watch-example-basic:
|
|
||||||
@ $(MAKE) example-basic 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)
|
||||||
|
@@ -1,84 +1,9 @@
|
|||||||
|
|
||||||
import Editor, { Mark, Raw } from '../..'
|
import Editor, { Raw } from '../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import keycode from 'keycode'
|
import keycode from 'keycode'
|
||||||
|
import state from './state.json'
|
||||||
/**
|
|
||||||
* Define the initial state.
|
|
||||||
*
|
|
||||||
* @type {Object} state
|
|
||||||
*/
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'The editor gives you full control over the logic you can add. For example, it\'s fairly common to want to add markdown-like shortcuts to editors. So that, when you start a line with "> " you get a blockquote that looks like this:',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block-quote',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'A wise quote.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'Order when you start a line with "## " you get a level-two heading, like this:',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'heading-two',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'Try it out!'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'Try it out for yourself! Try starting a new line with ">", "-", or "#"s.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define our example app.
|
* Define our example app.
|
||||||
|
69
examples/auto-markdown/state.json
Normal file
69
examples/auto-markdown/state.json
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "The editor gives you full control over the logic you can add. For example, it's fairly common to want to add markdown-like shortcuts to editors. So that, when you start a line with \"> \" you get a blockquote that looks like this:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "block-quote",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "A wise quote."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Order when you start a line with \"## \" you get a level-two heading, like this:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "heading-two",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Try it out!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Try it out for yourself! Try starting a new line with \">\", \"-\", or \"#\"s."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -1,24 +0,0 @@
|
|||||||
|
|
||||||
html {
|
|
||||||
background: #eee;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
background: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
max-width: 40em;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
main * + * {
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Editor | Basic Example</title>
|
|
||||||
<link rel="stylesheet" href="index.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main></main>
|
|
||||||
<script src="build.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -1,123 +0,0 @@
|
|||||||
|
|
||||||
import Editor, { State, Raw } from '../..'
|
|
||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* State.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'code',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'A\nfew\nlines\nof\ncode.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'A '
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'simple',
|
|
||||||
marks: [
|
|
||||||
{
|
|
||||||
type: 'bold'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: ' paragraph of text.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* App.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class App extends React.Component {
|
|
||||||
|
|
||||||
state = {
|
|
||||||
state: Raw.deserialize(state)
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Editor
|
|
||||||
renderNode={node => this.renderNode(node)}
|
|
||||||
renderMark={mark => this.renderMark(mark)}
|
|
||||||
state={this.state.state}
|
|
||||||
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 })
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderNode(node) {
|
|
||||||
switch (node.type) {
|
|
||||||
case 'code': {
|
|
||||||
return (props) => {
|
|
||||||
return (
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{props.children}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'paragraph': {
|
|
||||||
return (props) => {
|
|
||||||
return (
|
|
||||||
<p>
|
|
||||||
{props.children}
|
|
||||||
</p>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMark(mark) {
|
|
||||||
switch (mark.type) {
|
|
||||||
case 'bold': {
|
|
||||||
return {
|
|
||||||
fontWeight: 'bold'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attach.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const app = <App />
|
|
||||||
const root = document.body.querySelector('main')
|
|
||||||
ReactDOM.render(app, root)
|
|
@@ -2,14 +2,7 @@
|
|||||||
import Editor, { Character, Document, Element, State, Text } from '../..'
|
import Editor, { Character, Document, Element, State, Text } from '../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
import state from './state.json'
|
||||||
/**
|
|
||||||
* The initial editor state.
|
|
||||||
*
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
|
|
||||||
const state = 'This is editable plain text, just like a <textarea>!'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper to deserialize a string into an editor state.
|
* A helper to deserialize a string into an editor state.
|
||||||
@@ -52,17 +45,29 @@ function serialize(state) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The example's app.
|
* The example app.
|
||||||
*
|
*
|
||||||
* @type {Component} App
|
* @type {Component} App
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize the initial editor state.
|
||||||
|
*
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
state: deserialize(state)
|
state: deserialize(state)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the editor.
|
||||||
|
*
|
||||||
|
* @return {Component} component
|
||||||
|
*/
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Editor
|
<Editor
|
||||||
@@ -82,7 +87,7 @@ class App extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach.
|
* Mount the example app.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const app = <App />
|
const app = <App />
|
||||||
|
1
examples/plain-text/state.json
Normal file
1
examples/plain-text/state.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"This is editable plain text, just like a <textarea>!"
|
@@ -2,110 +2,7 @@
|
|||||||
import Editor, { Mark, Raw } from '../..'
|
import Editor, { Mark, Raw } from '../..'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
|
import state from './state.json'
|
||||||
/**
|
|
||||||
* State.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const state = {
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'This is editable '
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'rich',
|
|
||||||
marks: [
|
|
||||||
{
|
|
||||||
type: 'bold'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: ' text, '
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'much',
|
|
||||||
marks: [
|
|
||||||
{
|
|
||||||
type: 'italic'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: ' better than a '
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '<textarea>',
|
|
||||||
marks: [
|
|
||||||
{
|
|
||||||
type: 'code'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '!'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'Since it\'s rich text, you can do things like turn a selection of text ',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'bold',
|
|
||||||
marks: [
|
|
||||||
{
|
|
||||||
type: 'bold'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},{
|
|
||||||
text: ', or add a semanticlly rendered block quote in the middle of the page, like this:'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'block-quote',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'A wise quote.'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
ranges: [
|
|
||||||
{
|
|
||||||
text: 'Try it out for yourself!'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App.
|
* App.
|
||||||
|
99
examples/rich-text/state.json
Normal file
99
examples/rich-text/state.json
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "This is editable "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "rich",
|
||||||
|
"marks": [
|
||||||
|
{
|
||||||
|
"type": "bold"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": " text, "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "much",
|
||||||
|
"marks": [
|
||||||
|
{
|
||||||
|
"type": "italic"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": " better than a "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "<textarea>",
|
||||||
|
"marks": [
|
||||||
|
{
|
||||||
|
"type": "code"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Since it's rich text, you can do things like turn a selection of text ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "bold",
|
||||||
|
"marks": [
|
||||||
|
{
|
||||||
|
"type": "bold"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},{
|
||||||
|
"text": ", or add a semanticlly rendered block quote in the middle of the page, like this:"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "block-quote",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "A wise quote."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"text": "Try it out for yourself!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -34,6 +34,7 @@ const Node = {
|
|||||||
|
|
||||||
deleteAtRange(range) {
|
deleteAtRange(range) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// If the range is collapsed, there's nothing to do.
|
// If the range is collapsed, there's nothing to do.
|
||||||
if (range.isCollapsed) return node
|
if (range.isCollapsed) return node
|
||||||
@@ -123,6 +124,7 @@ const Node = {
|
|||||||
|
|
||||||
deleteBackwardAtRange(range, n = 1) {
|
deleteBackwardAtRange(range, n = 1) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// When collapsed at the start of the node, there's nothing to do.
|
// When collapsed at the start of the node, there's nothing to do.
|
||||||
if (range.isCollapsed && range.isAtStartOf(node)) return node
|
if (range.isCollapsed && range.isAtStartOf(node)) return node
|
||||||
@@ -160,6 +162,7 @@ const Node = {
|
|||||||
|
|
||||||
deleteForwardAtRange(range, n = 1) {
|
deleteForwardAtRange(range, n = 1) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// When collapsed at the end of the node, there's nothing to do.
|
// When collapsed at the end of the node, there's nothing to do.
|
||||||
if (range.isCollapsed && range.isAtEndOf(node)) return node
|
if (range.isCollapsed && range.isAtEndOf(node)) return node
|
||||||
@@ -231,6 +234,7 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
getCharactersAtRange(range) {
|
getCharactersAtRange(range) {
|
||||||
|
range = range.normalize(this)
|
||||||
const texts = this.getTextNodesAtRange(range)
|
const texts = this.getTextNodesAtRange(range)
|
||||||
let list = new List()
|
let list = new List()
|
||||||
|
|
||||||
@@ -272,6 +276,7 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
getMarksAtRange(range) {
|
getMarksAtRange(range) {
|
||||||
|
range = range.normalize(this)
|
||||||
const { startKey, startOffset, endKey } = range
|
const { startKey, startOffset, endKey } = range
|
||||||
|
|
||||||
// If the selection isn't set, return nothing.
|
// If the selection isn't set, return nothing.
|
||||||
@@ -470,6 +475,7 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
getTextNodesAtRange(range) {
|
getTextNodesAtRange(range) {
|
||||||
|
range = range.normalize(this)
|
||||||
const { startKey, endKey } = range
|
const { startKey, endKey } = range
|
||||||
if (startKey == null || endKey == null) return new OrderedMap()
|
if (startKey == null || endKey == null) return new OrderedMap()
|
||||||
|
|
||||||
@@ -494,6 +500,8 @@ const Node = {
|
|||||||
|
|
||||||
getWrappingNodesAtRange(range) {
|
getWrappingNodesAtRange(range) {
|
||||||
const node = this
|
const node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
const texts = node.getTextNodesAtRange(range)
|
const texts = node.getTextNodesAtRange(range)
|
||||||
const parents = texts.map((text) => {
|
const parents = texts.map((text) => {
|
||||||
return node.nodes.includes(text) ? node : node.getParentNode(text)
|
return node.nodes.includes(text) ? node : node.getParentNode(text)
|
||||||
@@ -533,6 +541,7 @@ const Node = {
|
|||||||
|
|
||||||
insertTextAtRange(range, text) {
|
insertTextAtRange(range, text) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// When still expanded, remove the current range first.
|
// When still expanded, remove the current range first.
|
||||||
if (range.isExpanded) {
|
if (range.isExpanded) {
|
||||||
@@ -581,6 +590,7 @@ const Node = {
|
|||||||
|
|
||||||
markAtRange(range, mark) {
|
markAtRange(range, mark) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// Allow for just passing a type for convenience.
|
// Allow for just passing a type for convenience.
|
||||||
if (typeof mark == 'string') {
|
if (typeof mark == 'string') {
|
||||||
@@ -694,6 +704,8 @@ const Node = {
|
|||||||
|
|
||||||
setTypeAtRange(range, type) {
|
setTypeAtRange(range, type) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
const texts = node.getTextNodesAtRange(range)
|
const texts = node.getTextNodesAtRange(range)
|
||||||
let parents = new OrderedSet()
|
let parents = new OrderedSet()
|
||||||
|
|
||||||
@@ -725,6 +737,7 @@ const Node = {
|
|||||||
|
|
||||||
splitAtRange(range) {
|
splitAtRange(range) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// If the range is expanded, remove it first.
|
// If the range is expanded, remove it first.
|
||||||
if (range.isExpanded) {
|
if (range.isExpanded) {
|
||||||
@@ -786,6 +799,7 @@ const Node = {
|
|||||||
|
|
||||||
unmarkAtRange(range, mark) {
|
unmarkAtRange(range, mark) {
|
||||||
let node = this
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// Allow for just passing a type for convenience.
|
// Allow for just passing a type for convenience.
|
||||||
if (typeof mark == 'string') {
|
if (typeof mark == 'string') {
|
||||||
@@ -853,6 +867,8 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
wrapAtRange(range, parent) {
|
wrapAtRange(range, parent) {
|
||||||
|
let node = this
|
||||||
|
range = range.normalize(node)
|
||||||
|
|
||||||
// Allow for the parent to by just a type.
|
// Allow for the parent to by just a type.
|
||||||
if (typeof parent == 'string') {
|
if (typeof parent == 'string') {
|
||||||
@@ -860,7 +876,7 @@ const Node = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the child to the parent's nodes.
|
// Add the child to the parent's nodes.
|
||||||
const child = this.findNode(key)
|
const child = node.findNode(key)
|
||||||
parent = node.nodes.set(child.key, child)
|
parent = node.nodes.set(child.key, child)
|
||||||
|
|
||||||
// Remove the child from this node.
|
// Remove the child from this node.
|
||||||
|
@@ -126,6 +126,54 @@ class Selection extends SelectionRecord {
|
|||||||
return endKey == last.key && endOffset == last.length
|
return endKey == last.key && endOffset == last.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize the selection, relative to a `node`, ensuring that the anchor
|
||||||
|
* and focus nodes of the selection always refer to leaf text nodes.
|
||||||
|
*
|
||||||
|
* @param {Node} node
|
||||||
|
* @return {Selection} selection
|
||||||
|
*/
|
||||||
|
|
||||||
|
normalize(node) {
|
||||||
|
let selection = this
|
||||||
|
let { anchorKey, anchorOffset, focusKey, focusOffset } = selection
|
||||||
|
|
||||||
|
// If the selection isn't formed yet, abort.
|
||||||
|
if (anchorKey == null || focusKey == null) return selection
|
||||||
|
|
||||||
|
// Asset that the anchor and focus nodes exist in the node tree.
|
||||||
|
node.assertHasNode(anchorKey)
|
||||||
|
node.assertHasNode(focusKey)
|
||||||
|
let anchorNode = node.getNode(anchorKey)
|
||||||
|
let focusNode = node.getNode(focusKey)
|
||||||
|
|
||||||
|
// If the anchor node isn't a text node, match it to one.
|
||||||
|
if (anchorNode.type != 'text') {
|
||||||
|
anchorNode = node.getNodeAtOffset(anchorOffset)
|
||||||
|
let parent = node.getParentNode(anchorNode)
|
||||||
|
let offset = parent.getNodeOffset(anchorNode)
|
||||||
|
anchorOffset = anchorOffset - offset
|
||||||
|
anchorKey = anchorNode.key
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the focus node isn't a text node, match it to one.
|
||||||
|
if (focusNode.type != 'text') {
|
||||||
|
focusNode = node.getNodeAtOffset(focusOffset)
|
||||||
|
let parent = node.getParentNode(focusNode)
|
||||||
|
let offset = parent.getNodeOffset(focusNode)
|
||||||
|
focusOffset = focusOffset - offset
|
||||||
|
focusKey = focusNode.key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge in any updated properties.
|
||||||
|
return selection.merge({
|
||||||
|
anchorKey,
|
||||||
|
anchorOffset,
|
||||||
|
focusKey,
|
||||||
|
focusOffset
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the selection to a set of `properties`.
|
* Move the selection to a set of `properties`.
|
||||||
*
|
*
|
||||||
|
Reference in New Issue
Block a user