diff --git a/docs/Readme.md b/docs/Readme.md
index 72d049187..024458432 100644
--- a/docs/Readme.md
+++ b/docs/Readme.md
@@ -38,6 +38,7 @@
* [Mark](./reference//slate/mark.md)
* [Node](./reference/slate/node.md)
* [Operation](./reference/slate/operation.md)
+* [Point](./reference/slate/point.md)
* [Range](./reference/slate/range.md)
* [Schema](./reference/slate/schema.md)
* [Text](./reference/slate/text.md)
diff --git a/docs/guides/changes.md b/docs/guides/changes.md
index 30f8c17f6..8c4e36cc3 100644
--- a/docs/guides/changes.md
+++ b/docs/guides/changes.md
@@ -19,12 +19,13 @@ To solve this, Slate has very expressive, chainable changes. Like this:
```js
change
.focus()
- .selectAll()
+ .moveToRangeOfDocument()
.delete()
.insertText('A bit of rich text, followed by...')
- .moveToOffsets(10, 14)
+ .moveTo(10)
+ .moveFocusForward(4)
.addMark('bold')
- .collapseToEndOfBlock()
+ .moveToEndOfBlock()
.insertBlock({
type: 'image',
isVoid: true,
@@ -63,7 +64,7 @@ These are changes like `delete()`, `addMark()`, `insertBlock()`, etc. that are t
### On the Selection
-These are changes like `blur()`, `collapseToStart()`, `moveToRangeOf()`, etc. that change the `value.selection` model and update the user's cursor without affecting the content of the document.
+These are changes like `blur()`, `moveToStart()`, `moveToRangeOfNode()`, etc. that change the `value.selection` model and update the user's cursor without affecting the content of the document.
### On a Specific Node
@@ -151,7 +152,7 @@ That said, if that's okay with you, you can make changes manually by using the `
```js
const change = value
.change()
- .selectAll()
+ .moveToRangeOfDocument()
.delete()
const newValue = change.value
diff --git a/docs/guides/data-model.md b/docs/guides/data-model.md
index d4657e4cb..7d47e2707 100644
--- a/docs/guides/data-model.md
+++ b/docs/guides/data-model.md
@@ -90,41 +90,49 @@ That all sounds pretty complex, but you don't have to think about it much, as lo
* Marks represent **unordered**, character-level formatting.
* Inlines represent **contiguous**, semantic elements in the document.
-## Ranges and "The Selection"
+## Ranges, Points and "The Selection"
Just like in the DOM, you can reference a part of the document using a `Range`. And there's one special range that Slate keeps track of that refers to the user's current cursor selection, called the "selection".
-Ranges are defined by an "anchor" and "focus" point. The anchor is where the range starts, and the focus is where it ends. And each point is a combination of a "path" or "key" referencing a specific node, and an "offset". This ends up looking like this:
+Ranges are defined by two `Point`s, an "anchor" point and a "focus" point. The anchor is where the range starts, and the focus is where it ends. And each point is a combination of a "path" or "key" referencing a specific node, and an "offset". This ends up looking like this:
```js
const range = Range.create({
- anchorKey: 'node-a',
- anchorOffset: 0,
- focusKey: 'node-b',
- focusOffset: 4,
- isBackward: false,
+ anchor: {
+ key: 'node-a',
+ offset: 0,
+ },
+ focus: {
+ key: 'node-b',
+ offset: 4,
+ },
})
const range = Range.create({
- anchorPath: [0, 2, 1],
- anchorOffset: 0,
- focusPath: [0, 3, 2],
- focusOffset: 4,
- isBackward: false,
+ anchor: {
+ path: [0, 2, 1],
+ offset: 0,
+ },
+ focus: {
+ path: [0, 3, 2],
+ offset: 4,
+ },
})
```
The more readable `node-a` name is just pseudocode, because Slate uses auto-incrementing numerical strings by defaultβ`'1', '2', '3', ...` But the important part is that every node has a unique `key` property, and a range can reference nodes by their keys.
-The terms "anchor" and "focus" are borrowed from the DOM, where they mean the same thing. The anchor point isn't always _before_ the focus point in the document. Just like in the DOM, it depends on whether the range is backwards or forwards.
+The terms "anchor" and "focus" are borrowed from the DOM API. The anchor point isn't always _before_ the focus point in the document. Just like in the DOM, it depends on whether the range is backwards or forwards.
Here's how MDN explains it:
> A user may make a selection from left to right (in document order) or right to left (reverse of document order). The anchor is where the user began the selection and the focus is where the user ends the selection. If you make a selection with a desktop mouse, the anchor is placed where you pressed the mouse button and the focus is placed where you released the mouse button. Anchor and focus should not be confused with the start and end positions of a selection, since anchor can be placed before the focus or vice versa, depending on the direction you made your selection.
> β [`Selection`, MDN](https://developer.mozilla.org/en-US/docs/Web/API/Selection)
-To make dealing with ranges easier though, they also provide "start" and "end" properties that take whether the range is forward or backward into account. The `startKey` and `startPath` will always be before the `endKey` and `endPath` in the document.
+To make dealing with ranges easier though, `Range` objects also provide `start` and `end` point properties that take whether the range is forward or backward into account. The `start.key` and `start.path` will always be before the `end.key` and `end.path` in the document.
-One important thing to note is that the anchor and focus points of ranges **always reference the "leaf-most" text nodes**. They never reference blocks or inlines, always their child text nodes. This makes dealing with ranges a _lot_ easier.
+These `start` and `end` points are what most of your logic will be based on, since you rarely care which side of the selection is "extendable".
+
+One important thing to note is that the anchor and focus points of ranges **always reference the "leaf-most" text nodes** in a document. They never reference blocks or inlines, always their child text nodes. This is different than in the DOM API, but it makes dealing with ranges a _lot_ easier because there are less edge cases to handle.
> π For more info, check out the [`Range` reference](../reference/slate/range.md).
diff --git a/docs/guides/plugins.md b/docs/guides/plugins.md
index 28800280d..500d085e5 100644
--- a/docs/guides/plugins.md
+++ b/docs/guides/plugins.md
@@ -21,7 +21,7 @@ Here's a really simple plugin:
},
onClick(event, change, editor) {
if (change.value.isBlurred) {
- change.selectAll().focus()
+ change.moveToRangeOfDocument().focus()
}
}
}
@@ -287,7 +287,10 @@ const plugins = [
AddMark({
hotkey: 'cmd+opt+c',
change: change => {
- change.addMark({ type: 'comment', data: { id: userId } }).selectAll()
+ change
+ .addMark({ type: 'comment', data: { id: userId } })
+ .moveBackward(3)
+ .delete()
},
}),
]
diff --git a/docs/images/auto-markdown-example.png b/docs/images/auto-markdown-example.png
deleted file mode 100644
index 887809302..000000000
Binary files a/docs/images/auto-markdown-example.png and /dev/null differ
diff --git a/docs/images/code-highlighting-example.png b/docs/images/code-highlighting-example.png
deleted file mode 100644
index 249e0a883..000000000
Binary files a/docs/images/code-highlighting-example.png and /dev/null differ
diff --git a/docs/images/forced-layout-example.png b/docs/images/forced-layout-example.png
deleted file mode 100644
index 75fd15405..000000000
Binary files a/docs/images/forced-layout-example.png and /dev/null differ
diff --git a/docs/images/hovering-menu-example.png b/docs/images/hovering-menu-example.png
deleted file mode 100644
index 0bb8a06f7..000000000
Binary files a/docs/images/hovering-menu-example.png and /dev/null differ
diff --git a/docs/images/images-example.png b/docs/images/images-example.png
deleted file mode 100644
index 0f82e2c05..000000000
Binary files a/docs/images/images-example.png and /dev/null differ
diff --git a/docs/images/links-example.png b/docs/images/links-example.png
deleted file mode 100644
index 5d9a91c42..000000000
Binary files a/docs/images/links-example.png and /dev/null differ
diff --git a/docs/images/paste-html-example.png b/docs/images/paste-html-example.png
deleted file mode 100644
index c82e6bf19..000000000
Binary files a/docs/images/paste-html-example.png and /dev/null differ
diff --git a/docs/images/plain-text-example.png b/docs/images/plain-text-example.png
deleted file mode 100644
index cd0b7cc06..000000000
Binary files a/docs/images/plain-text-example.png and /dev/null differ
diff --git a/docs/images/read-only-example.png b/docs/images/read-only-example.png
deleted file mode 100644
index c1abd25bc..000000000
Binary files a/docs/images/read-only-example.png and /dev/null differ
diff --git a/docs/images/rich-text-example.png b/docs/images/rich-text-example.png
deleted file mode 100644
index e0b82716c..000000000
Binary files a/docs/images/rich-text-example.png and /dev/null differ
diff --git a/docs/images/tables-example.png b/docs/images/tables-example.png
deleted file mode 100644
index 733c41468..000000000
Binary files a/docs/images/tables-example.png and /dev/null differ
diff --git a/docs/reference/slate-react/custom-nodes.md b/docs/reference/slate-react/custom-nodes.md
index df54c8d9b..044eda620 100644
--- a/docs/reference/slate-react/custom-nodes.md
+++ b/docs/reference/slate-react/custom-nodes.md
@@ -57,7 +57,7 @@ const value = editor.value
```js
editor.change(change => {
- change.selectAll().delete()
+ change.moveToRangeOfDocument().delete()
})
```
diff --git a/docs/reference/slate/point.md b/docs/reference/slate/point.md
new file mode 100644
index 000000000..66c7154cf
--- /dev/null
+++ b/docs/reference/slate/point.md
@@ -0,0 +1,173 @@
+# `Point`
+
+```js
+import { Point } from 'slate'
+```
+
+A point in a Slate [`Document`](./document.md). Points in Slate are inspired by the [DOM Range API](https://developer.mozilla.org/en-US/docs/Web/API/Range), with terms like "offset".
+
+## Properties
+
+```js
+Point({
+ key: String,
+ path: List,
+ offset: Number,
+})
+```
+
+### `key`
+
+`String`
+
+The key of the text node at the point's point.
+
+### `path`
+
+`List`
+
+The path to the text node at the point's point.
+
+### `object`
+
+`String`
+
+A string with a value of `'point'`.
+
+### `offset`
+
+`Number`
+
+The number of characters from the start of the text node at the point's point.
+
+## Computed Properties
+
+### `isSet`
+
+`Boolean`
+
+Whether the key, path and offset of a point is not `null`.
+
+### `isUnset`
+
+`Boolean`
+
+Whether any of the key, path or offset of a point is `null`.
+
+## Static Methods
+
+### `Point.create`
+
+`Point.create(properties: Object|Point) => Point`
+
+Create a new `Point` with `properties`.
+
+### `Point.createProperties`
+
+`Point.createProperties(object: Object|Point) => Object`
+
+Create a new dictionary of point properties from an `object`.
+
+### `Point.fromJSON`
+
+`Point.fromJSON(object: Object) => Point`
+
+Create a point from a JSON `object`.
+
+### `Point.isPoint`
+
+`Point.isPoint(value: Any) => Boolean`
+
+Check whether a `value` is a `Point`.
+
+## Instance Methods
+
+### `toJSON`
+
+`toJSON() => Object`
+
+Return a JSON representation of the point.
+
+## Checking Methods
+
+### `isAtEndOfNode`
+
+`isAtEndOfNode(node: Node) => Boolean`
+
+Determine whether the point is at the end of a `node`.
+
+### `isAtStartOfNode`
+
+`isAtStartOfNode(node: Node) => Boolean`
+
+Determine whether the point is at the start of a `node`.
+
+### `isInNode`
+
+`isInNode(node: Node) => Boolean`
+
+Determine whether a point is inside a `node`.
+
+## Mutating Methods
+
+### `moveBackward`
+
+`moveBackward(n: Number) => Point`
+
+Return a new point with its offset moved backwards by `n` characters.
+
+### `moveForward`
+
+`moveForward(n: Number) => Point`
+
+Return a new point with its offset moved forwards by `n` characters.
+
+### `moveTo`
+
+`moveTo(path: List, offset: Number) => Point`
+`moveTo(key: String, offset: Number) => Point`
+`moveTo(offset: Number) => Point`
+
+Return a new point with its `path`, `key` and `offset` set to new values.
+
+> π€ When using `point.moveTo`, since the point isn't aware of the document, it's possible it will become "unset" if the path or key changes and need to be re-normalized relative to the document using `point.normalize(document)`.
+
+### `moveToEndOfNode`
+
+`moveToEndOfNode(node: Node) => Point`
+
+Return a new point at the end of a `node`.
+
+> π€ This method may need to be followed by `point.normalize(document)`, like [`moveTo`](#moveto).
+
+### `moveToStartOfNode`
+
+`moveToStartOfNode(node: Node) => Point`
+
+Return a new point at the start of a `node`.
+
+> π€ This method may need to be followed by `point.normalize(document)`, like [`moveTo`](#moveto).
+
+### `normalize`
+
+`normalize(node: Node) => Point`
+
+Normalize the point relative to a `node`, ensuring that its key and path are in sync, that its offset is valid, and that it references a leaf text node.
+
+### `setKey`
+
+`setKey(key: String|Null) => Point`
+
+Return a new point with a new `key`.
+
+### `setOffset`
+
+`setOffset(offset: Number|Null) => Point`
+
+Return a new point with a new `offset`.
+
+### `setPath`
+
+`setPath(path: List|Array|Null) => Point`
+
+Return a new point with a new `path`.
diff --git a/docs/reference/slate/range.md b/docs/reference/slate/range.md
index 0909ae7ad..942f91059 100644
--- a/docs/reference/slate/range.md
+++ b/docs/reference/slate/range.md
@@ -14,58 +14,23 @@ Often times, you don't need to specifically know which point is the "anchor" and
```js
Range({
- anchorKey: String,
- anchorPath: List,
- anchorOffset: Number,
- focusKey: String,
- focusPath: List,
- focusOffset: Number,
+ anchor: Point,
+ focus: Point,
isFocused: Boolean,
- isBackward: Boolean,
})
```
-### `anchorKey`
+### `anchor`
-`String`
+`Point`
-The key of the text node at the range's anchor point.
+The range's anchor point.
-### `anchorPath`
+### `focus`
-`List`
+`Point`
-The path to the text node at the range's anchor point.
-
-### `anchorOffset`
-
-`Number`
-
-The number of characters from the start of the text node at the range's anchor point.
-
-### `focusKey`
-
-`String`
-
-The key of the text node at the range's focus point.
-
-### `focusPath`
-
-`List`
-
-The path to the text node at the range's focus point.
-
-### `focusOffset`
-
-`Number`
-
-The number of characters from the start of the text node at the range's focus point.
-
-### `isBackward`
-
-`Boolean`
-
-Whether the range is backward. A range is considered "backward" when its focus point references a location earlier in the document than its anchor point.
+The range's focus point.
### `isFocused`
@@ -83,6 +48,16 @@ A string with a value of `'range'`.
These properties aren't supplied when creating a range, but are instead computed based on the real properties.
+### `end`
+
+Either the `anchor` or the `focus` point, depending on which comes last in the document order.
+
+### `isBackward`
+
+`Boolean`
+
+Whether the range is backward. A range is considered "backward" when its focus point references a location earlier in the document than its anchor point.
+
### `isBlurred`
`Boolean`
@@ -107,19 +82,21 @@ The opposite of `isCollapsed`, for convenience.
The opposite of `isBackward`, for convenience.
-### `startKey`
+### `isSet`
-### `startPath`
+`Boolean`
-### `startOffset`
+Whether both the `anchor` and `focus` points are set.
-### `endKey`
+### `isUnset`
-### `endPath`
+`Boolean`
-### `endOffset`
+Whether either the `anchor` and `focus` points are unset.
-A few convenience properties for accessing the first and last point of the range. When the range is forward, `start` refers to the `anchor` point and `end` refers to the `focus` point. And when it's backward they are reversed.
+### `start`
+
+Either the `anchor` or the `focus` point, depending on which comes first in the document order.
## Static Methods
@@ -129,6 +106,12 @@ A few convenience properties for accessing the first and last point of the range
Create a new `Range` instance with `properties`.
+### `Range.createProperties`
+
+`Range.createProperties(object: Object|Range) => Object`
+
+Create a new dictionary of range properties from an `object`.
+
### `Range.fromJSON`
`Range.fromJSON(object: Object) => Range`
@@ -137,9 +120,9 @@ Create a range from a JSON `object`.
### `Range.isRange`
-`Range.isRange(maybeRange: Any) => Boolean`
+`Range.isRange(value: Any) => Boolean`
-Returns a boolean if the passed in argument is a `Range`.
+Check whether a `value` is a `Range`.
## Instance Methods
@@ -147,42 +130,94 @@ Returns a boolean if the passed in argument is a `Range`.
`toJSON() => Object`
-Returns a JSON representation of the range.
+Return a JSON representation of the range.
-## Checking Methods
+## Mutating Methods
-### `has{Edge}AtStartOf`
+### `move{Point}Backward`
-`has{Edge}AtStartOf(node: Node) => Boolean`
+`move{Point}Backward(n: Number) => Range`
-Determine whether a range has an edge at the start of a `node`. Where `{Edge}` can be one of: `Anchor`, `Focus`, `Start`, `End` or `Edge` (referring to either point).
+Move the `{Point}` of the range backwards by `n` characters. The `{Point}` can be one of: `Anchor`, `Focus`, `Start`, `End`, or ommited to move both the `anchor` and `focus` point at once.
-### `has{Edge}AtEndOf`
+### `move{Point}Forward`
-`has{Edge}AtEndOf(node: Node) => Boolean`
+`move{Point}Forward(n: Number) => Range`
-Determine whether a range has an edge at the end of a `node`. Where `{Edge}` can be one of: `Anchor`, `Focus`, `Start`, `End` or `Edge` (referring to either point).
+Move the `{Point}` of the range forwards by `n` characters. The `{Point}` can be one of: `Anchor`, `Focus`, `Start`, `End`, or ommited to move both the `anchor` and `focus` point at once.
-### `has{Edge}Between`
+### `move{Point}To`
-`has{Edge}Between(node: Node, start: Number, end: Number) => Boolean`
+`move{Point}To(path: List, offset: Number) => Range`
+`move{Point}To(key: String, offset: Number) => Range`
+`move{Point}To(offset: Number) => Range`
-Determine whether a range has an edge in a `node` between its `start` and `end` offset. Where `{Edge}` can be one of: `Anchor`, `Focus`, `Start`, `End` or `Edge` (referring to either point).
+Move the `{Point}` of the range to a new `key`, `path` and `offset`. The `{Point}` can be one of: `Anchor`, `Focus`, `Start`, `End`, or ommited to move both the `anchor` and `focus` point at once.
-### `has{Edge}In`
+> π€ When using `range.move{Point}To`, since the range isn't aware of the document, it's possible it will become "unset" if the path or key changes and need to be re-normalized relative to the document using `range.normalize(document)`.
-`has{Edge}In(node: Node) => Boolean`
+### `move{Point}ToEndOfNode`
-Determine whether a range has an edge inside a `node`. Where `{Edge}` can be one of: `Anchor`, `Focus`, `Start`, `End` or `Edge` (referring to either point).
+`move{Point}ToEndOfNode(node: Node) => Range`
-### `isAtStartOf`
+Move the `{Point}` to the end of a `node`. The `{Point}` can be one of: `Anchor`, `Focus`, `Start`, `End`, or ommited to move both the `anchor` and `focus` point at once.
-`isAtStartOf(node: Node) => Boolean`
+> π€ This method may need to be followed by `point.normalize(document)`, like [`move{Point}To`](#movepointto).
-Determine whether the range is at the start of a `node`.
+### `move{Point}ToStartOfNode`
-### `isAtEndOf`
+`move{Point}ToStartOfNode(node: Node) => Range`
-`isAtEndOf(node: Node) => Boolean`
+Move the `{Point}` to the start of a `node`. The `{Point}` can be one of: `Anchor`, `Focus`, `Start`, `End`, or ommited to move both the `anchor` and `focus` point at once.
-Determine whether the range is at the end of a `node`.
+> π€ This method may need to be followed by `point.normalize(document)`, like [`move{Point}To`](#movepointto).
+
+### `moveTo{Point}`
+
+`moveTo{Point}() => Range`
+
+Move both points of the range to `{Point}`, collapsing it. The `{Point}` can be one of: `Anchor`, `Focus`, `Start` or `End`.
+
+### `moveToRangeOfNode`
+
+`moveToRangeOfNode(node: Node) => Range`
+
+Move the range to be spanning the entirity of a `node`, by placing its `anchor` point at the start of the node and its `focus` point at the end of the node.
+
+> π€ This method may need to be followed by `point.normalize(document)`, like [`move{Point}To`](#movepointto).
+
+### `normalize`
+
+`normalize(node: Node) => Range`
+
+Normalize the range relative to a `node`, ensuring that its anchor and focus points exist in the `node`, that their keys and paths are in sync, that their offsets are valid, and that they references leaf text nodes.
+
+### `setAnchor`
+
+`setAnchor(anchor: Point) => Range`
+
+Return a new range with a new `anchor` point.
+
+### `setEnd`
+
+`setEnd(end: Point) => Range`
+
+Return a new range with a new `end` point.
+
+### `setFocus`
+
+`setFocus(focus: Point) => Range`
+
+Return a new range with a new `focus` point.
+
+### `setProperties`
+
+`setProperties(properties: Object|Range) => Range`
+
+Return a new range with new `properties` set.
+
+### `setStart`
+
+`setStart(start: Point) => Range`
+
+Return a new range with a new `start` point.
diff --git a/examples/code-highlighting/index.js b/examples/code-highlighting/index.js
index 3b58bc69e..e9a246ce6 100644
--- a/examples/code-highlighting/index.js
+++ b/examples/code-highlighting/index.js
@@ -173,10 +173,10 @@ class CodeHighlighting extends React.Component {
onKeyDown = (event, change) => {
const { value } = change
- const { startBlock } = value
+ const { selection, startBlock } = value
if (event.key != 'Enter') return
if (startBlock.type != 'code') return
- if (value.isExpanded) change.delete()
+ if (selection.isExpanded) change.delete()
change.insertText('\n')
return true
}
@@ -226,10 +226,14 @@ class CodeHighlighting extends React.Component {
if (typeof token != 'string') {
const range = {
- anchorKey: startText.key,
- anchorOffset: startOffset,
- focusKey: endText.key,
- focusOffset: endOffset,
+ anchor: {
+ key: startText.key,
+ offset: startOffset,
+ },
+ focus: {
+ key: endText.key,
+ offset: endOffset,
+ },
marks: [{ type: token.type }],
}
diff --git a/examples/emojis/index.js b/examples/emojis/index.js
index 42e40f4d5..1b201df93 100644
--- a/examples/emojis/index.js
+++ b/examples/emojis/index.js
@@ -150,7 +150,7 @@ class Emojis extends React.Component {
isVoid: true,
data: { code },
})
- .collapseToStartOfNextText()
+ .moveToStartOfNextText()
.focus()
this.onChange(change)
diff --git a/examples/images/index.js b/examples/images/index.js
index f0c0bc352..a1da01612 100644
--- a/examples/images/index.js
+++ b/examples/images/index.js
@@ -61,7 +61,7 @@ function insertImage(change, src, target) {
const schema = {
document: {
- last: { types: ['paragraph'] },
+ last: { type: 'paragraph' },
normalize: (change, reason, { node, child }) => {
switch (reason) {
case LAST_CHILD_TYPE_INVALID: {
diff --git a/examples/links/index.js b/examples/links/index.js
index 040a0a542..92d6bdfb1 100644
--- a/examples/links/index.js
+++ b/examples/links/index.js
@@ -19,7 +19,7 @@ function wrapLink(change, href) {
data: { href },
})
- change.collapseToEnd()
+ change.moveToEnd()
}
/**
@@ -157,7 +157,7 @@ class Links extends React.Component {
*/
onPaste = (event, change) => {
- if (change.value.isCollapsed) return
+ if (change.value.selection.isCollapsed) return
const transfer = getEventTransfer(event)
const { type, text } = transfer
diff --git a/examples/markdown-preview/index.js b/examples/markdown-preview/index.js
index f5c4957c4..dc314f233 100644
--- a/examples/markdown-preview/index.js
+++ b/examples/markdown-preview/index.js
@@ -183,10 +183,14 @@ class MarkdownPreview extends React.Component {
if (typeof token != 'string') {
const range = {
- anchorKey: startText.key,
- anchorOffset: startOffset,
- focusKey: endText.key,
- focusOffset: endOffset,
+ anchor: {
+ key: startText.key,
+ offset: startOffset,
+ },
+ focus: {
+ key: endText.key,
+ offset: endOffset,
+ },
marks: [{ type: token.type }],
}
diff --git a/examples/markdown-shortcuts/index.js b/examples/markdown-shortcuts/index.js
index f9c20731c..1e685691d 100644
--- a/examples/markdown-shortcuts/index.js
+++ b/examples/markdown-shortcuts/index.js
@@ -142,10 +142,12 @@ class MarkdownShortcuts extends React.Component {
onSpace = (event, change) => {
const { value } = change
- if (value.isExpanded) return
+ const { selection } = value
+ if (selection.isExpanded) return
- const { startBlock, startOffset } = value
- const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '')
+ const { startBlock } = value
+ const { start } = selection
+ const chars = startBlock.text.slice(0, start.offset).replace(/\s*/g, '')
const type = this.getType(chars)
if (!type) return
@@ -158,7 +160,7 @@ class MarkdownShortcuts extends React.Component {
change.wrapBlock('bulleted-list')
}
- change.extendToStartOf(startBlock).delete()
+ change.moveFocusToStartOf(startBlock).delete()
return true
}
@@ -172,8 +174,9 @@ class MarkdownShortcuts extends React.Component {
onBackspace = (event, change) => {
const { value } = change
- if (value.isExpanded) return
- if (value.startOffset != 0) return
+ const { selection } = value
+ if (selection.isExpanded) return
+ if (selection.start.offset != 0) return
const { startBlock } = value
if (startBlock.type == 'paragraph') return
@@ -198,12 +201,14 @@ class MarkdownShortcuts extends React.Component {
onEnter = (event, change) => {
const { value } = change
- if (value.isExpanded) return
+ const { selection } = value
+ const { start, end, isExpanded } = selection
+ if (isExpanded) return
- const { startBlock, startOffset, endOffset } = value
- if (startOffset == 0 && startBlock.text.length == 0)
+ const { startBlock } = value
+ if (start.offset == 0 && startBlock.text.length == 0)
return this.onBackspace(event, change)
- if (endOffset != startBlock.text.length) return
+ if (end.offset != startBlock.text.length) return
if (
startBlock.type != 'heading-one' &&
diff --git a/examples/search-highlighting/index.js b/examples/search-highlighting/index.js
index 1ff9d64ed..f2cbb4a41 100644
--- a/examples/search-highlighting/index.js
+++ b/examples/search-highlighting/index.js
@@ -125,10 +125,8 @@ class SearchHighlighting extends React.Component {
parts.forEach((part, i) => {
if (i != 0) {
decorations.push({
- anchorKey: key,
- anchorOffset: offset - string.length,
- focusKey: key,
- focusOffset: offset,
+ anchor: { key, offset: offset - string.length },
+ focus: { key, offset },
marks: [{ type: 'highlight' }],
isAtomic: true,
})
diff --git a/examples/tables/index.js b/examples/tables/index.js
index 6562ff4bd..de4cae57e 100644
--- a/examples/tables/index.js
+++ b/examples/tables/index.js
@@ -92,7 +92,8 @@ class Tables extends React.Component {
onBackspace = (event, change) => {
const { value } = change
- if (value.startOffset != 0) return
+ const { selection } = value
+ if (selection.start.offset != 0) return
event.preventDefault()
return true
}
@@ -116,7 +117,8 @@ class Tables extends React.Component {
onDelete = (event, change) => {
const { value } = change
- if (value.endOffset != value.startText.text.length) return
+ const { selection } = value
+ if (selection.end.offset != value.startText.text.length) return
event.preventDefault()
return true
}
@@ -169,10 +171,10 @@ class Tables extends React.Component {
onKeyDown = (event, change) => {
const { value } = change
const { document, selection } = value
- const { startKey } = selection
- const startNode = document.getDescendant(startKey)
+ const { start, isCollapsed } = selection
+ const startNode = document.getDescendant(start.key)
- if (selection.isAtStartOf(startNode)) {
+ if (isCollapsed && start.isAtStartOfNode(startNode)) {
const previous = document.getPreviousText(startNode.key)
const prevBlock = document.getClosestBlock(previous.key)
diff --git a/packages/slate-hyperscript/src/index.js b/packages/slate-hyperscript/src/index.js
index 3e1f23d9a..882be7bda 100644
--- a/packages/slate-hyperscript/src/index.js
+++ b/packages/slate-hyperscript/src/index.js
@@ -1,52 +1,71 @@
-import isEmpty from 'is-empty'
import isPlainObject from 'is-plain-object'
-import { Block, Document, Inline, Mark, Node, Range, Text, Value } from 'slate'
+import {
+ Block,
+ Document,
+ Inline,
+ Mark,
+ Node,
+ Point,
+ Range,
+ Text,
+ Value,
+} from 'slate'
/**
- * Create selection point constants, for comparison by reference.
+ * Point classes that can be created at different points in the document and
+ * then searched for afterwards, for creating ranges.
*
- * @type {Object}
+ * @type {Class}
*/
-const ANCHOR = {}
-const CURSOR = {}
-const FOCUS = {}
+class CursorPoint {
+ constructor() {
+ this.offset = null
+ }
+}
-/**
- * wrappers for decorator points, for comparison by instanceof,
- * and for composition into ranges (anchor.combine(focus), etc)
- */
+class AnchorPoint {
+ constructor(attrs = {}) {
+ const { key = null, offset = null, path = null } = attrs
+ this.key = key
+ this.offset = offset
+ this.path = path
+ }
+}
-class DecoratorPoint {
- constructor({ key, data }, marks) {
- this._key = key
+class FocusPoint {
+ constructor(attrs = {}) {
+ const { key = null, offset = null, path = null } = attrs
+ this.key = key
+ this.offset = offset
+ this.path = path
+ }
+}
+
+class DecorationPoint {
+ constructor(attrs) {
+ const { key = null, data = {}, marks } = attrs
+ this.id = key
+ this.offset = 0
this.marks = marks
this.attribs = data || {}
this.isAtomic = !!this.attribs.atomic
delete this.attribs.atomic
return this
}
- withPosition = offset => {
- this.offset = offset
- return this
- }
- addOffset = offset => {
- this.offset += offset
- return this
- }
- withKey = key => {
- this.key = key
- return this
- }
combine = focus => {
- if (!(focus instanceof DecoratorPoint))
+ if (!(focus instanceof DecorationPoint))
throw new Error('misaligned decorations')
return Range.create({
- anchorKey: this.key,
- focusKey: focus.key,
- anchorOffset: this.offset,
- focusOffset: focus.offset,
+ anchor: {
+ key: this.key,
+ offset: this.offset,
+ },
+ focus: {
+ key: focus.key,
+ offset: focus.offset,
+ },
marks: this.marks,
isAtomic: this.isAtomic,
...this.attribs,
@@ -62,7 +81,7 @@ class DecoratorPoint {
const CREATORS = {
anchor(tagName, attributes, children) {
- return ANCHOR
+ return new AnchorPoint(attributes)
},
block(tagName, attributes, children) {
@@ -73,7 +92,7 @@ const CREATORS = {
},
cursor(tagName, attributes, children) {
- return CURSOR
+ return new CursorPoint()
},
document(tagName, attributes, children) {
@@ -84,7 +103,7 @@ const CREATORS = {
},
focus(tagName, attributes, children) {
- return FOCUS
+ return new FocusPoint(attributes)
},
inline(tagName, attributes, children) {
@@ -102,109 +121,139 @@ const CREATORS = {
decoration(tagName, attributes, children) {
if (attributes.key) {
- return new DecoratorPoint(attributes, [{ type: tagName }])
+ return new DecorationPoint({
+ ...attributes,
+ marks: [{ type: tagName }],
+ })
}
- const nodes = createChildren(children, { key: attributes.key })
+ const nodes = createChildren(children)
+ const node = nodes[0]
+ const { __decorations = [] } = node
+ const __decoration = {
+ anchorOffset: 0,
+ focusOffset: nodes.reduce((len, n) => len + n.text.length, 0),
+ marks: [{ type: tagName }],
+ isAtomic: !!attributes.data.atomic,
+ }
- nodes[0].__decorations = (nodes[0].__decorations || []).concat([
- {
- anchorOffset: 0,
- focusOffset: nodes.reduce((len, n) => len + n.text.length, 0),
- marks: [{ type: tagName }],
- isAtomic: !!attributes.data.atomic,
- },
- ])
+ __decorations.push(__decoration)
+ node.__decorations = __decorations
return nodes
},
selection(tagName, attributes, children) {
- return Range.create(attributes)
+ const anchor = children.find(c => c instanceof AnchorPoint)
+ const focus = children.find(c => c instanceof FocusPoint)
+ const selection = Range.create({
+ ...attributes,
+ anchor: anchor && {
+ key: anchor.key,
+ offset: anchor.offset,
+ path: anchor.path,
+ },
+ focus: focus && {
+ key: focus.key,
+ offset: focus.offset,
+ path: focus.path,
+ },
+ })
+
+ return selection
},
value(tagName, attributes, children) {
const { data, normalize = true } = attributes
const document = children.find(Document.isDocument)
let selection = children.find(Range.isRange) || Range.create()
- const props = {}
+ let anchor
+ let focus
let decorations = []
- const partialDecorations = {}
+ const partials = {}
// Search the document's texts to see if any of them have the anchor or
- // focus information saved, so we can set the selection.
+ // focus information saved, or decorations applied.
if (document) {
document.getTexts().forEach(text => {
if (text.__anchor != null) {
- props.anchorKey = text.key
- props.anchorOffset = text.__anchor
- props.isFocused = true
+ anchor = Point.create({ key: text.key, offset: text.__anchor.offset })
}
if (text.__focus != null) {
- props.focusKey = text.key
- props.focusOffset = text.__focus
- props.isFocused = true
+ focus = Point.create({ key: text.key, offset: text.__focus.offset })
}
- })
- // now check for decorations and hoist them to the top
- document.getTexts().forEach(text => {
if (text.__decorations != null) {
- // add in all mark-like (keyless) decorations
- decorations = decorations.concat(
- text.__decorations.filter(d => d._key === undefined).map(d =>
- Range.create({
- ...d,
- anchorKey: text.key,
- focusKey: text.key,
+ text.__decorations.forEach(dec => {
+ const { id } = dec
+ let range
+
+ if (!id) {
+ range = Range.create({
+ anchor: {
+ key: text.key,
+ offset: dec.anchorOffset,
+ },
+ focus: {
+ key: text.key,
+ offset: dec.focusOffset,
+ },
+ marks: dec.marks,
+ isAtomic: dec.isAtomic,
})
- )
- )
+ } else if (partials[id]) {
+ const partial = partials[id]
+ delete partials[id]
- // store or combine partial decorations (keyed with anchor / focus)
- text.__decorations
- .filter(d => d._key !== undefined)
- .forEach(partial => {
- if (partialDecorations[partial._key]) {
- decorations.push(
- partialDecorations[partial._key].combine(
- partial.withKey(text.key)
- )
- )
+ range = Range.create({
+ anchor: {
+ key: partial.key,
+ offset: partial.offset,
+ },
+ focus: {
+ key: text.key,
+ offset: dec.offset,
+ },
+ marks: partial.marks,
+ isAtomic: partial.isAtomic,
+ })
+ } else {
+ dec.key = text.key
+ partials[id] = dec
+ }
- delete partialDecorations[partial._key]
- return
- }
-
- partialDecorations[partial._key] = partial.withKey(text.key)
- })
+ if (range) {
+ decorations.push(range)
+ }
+ })
}
})
}
- // should have no more parital decorations outstanding (all paired)
- if (Object.keys(partialDecorations).length > 0) {
+ if (Object.keys(partials).length > 0) {
throw new Error(
- `Slate hyperscript must have both an anchor and focus defined for each keyed decorator.`
+ `Slate hyperscript must have both a start and an end defined for each decoration using the \`key=\` prop.`
)
}
- if (props.anchorKey && !props.focusKey) {
+ if (anchor && !focus) {
throw new Error(
- `Slate hyperscript must have both \`\` and \`\` defined if one is defined, but you only defined \`\`. For collapsed selections, use \`\`.`
+ `Slate hyperscript ranges must have both \`\` and \`\` defined if one is defined, but you only defined \`\`. For collapsed selections, use \`\` instead.`
)
}
- if (!props.anchorKey && props.focusKey) {
+ if (!anchor && focus) {
throw new Error(
- `Slate hyperscript must have both \`\` and \`\` defined if one is defined, but you only defined \`\`. For collapsed selections, use \`\`.`
+ `Slate hyperscript ranges must have both \`\` and \`\` defined if one is defined, but you only defined \`\`. For collapsed selections, use \`\` instead.`
)
}
let value = Value.fromJSON({ data, document, selection }, { normalize })
- if (!isEmpty(props)) {
- selection = selection.merge(props).normalize(value.document)
+ if (anchor || focus) {
+ selection = selection.setPoints([anchor, focus])
+ selection = selection.merge({ isFocused: true })
+ selection = selection.normalize(value.document)
value = value.set('selection', selection)
}
@@ -336,36 +385,47 @@ function createChildren(children, options = {}) {
i += leaf.text.length
})
- if (__anchor != null) node.__anchor = __anchor + length
- if (__focus != null) node.__focus = __focus + length
+ if (__anchor != null) {
+ node.__anchor = new AnchorPoint()
+ node.__anchor.offset = __anchor.offset + length
+ }
+
+ if (__focus != null) {
+ node.__focus = new FocusPoint()
+ node.__focus.offset = __focus.offset + length
+ }
if (__decorations != null) {
- node.__decorations = (node.__decorations || []).concat(
- __decorations.map(
- d =>
- d instanceof DecoratorPoint
- ? d.addOffset(length)
- : {
- ...d,
- anchorOffset: d.anchorOffset + length,
- focusOffset: d.focusOffset + length,
- }
- )
- )
+ __decorations.forEach(d => {
+ if (d instanceof DecorationPoint) {
+ d.offset += length
+ } else {
+ d.anchorOffset += length
+ d.focusOffset += length
+ }
+ })
+
+ node.__decorations = node.__decorations || []
+ node.__decorations = node.__decorations.concat(__decorations)
}
length += child.text.length
}
- // If the child is a selection object store the current position.
- if (child == ANCHOR || child == CURSOR) node.__anchor = length
- if (child == FOCUS || child == CURSOR) node.__focus = length
+ if (child instanceof AnchorPoint || child instanceof CursorPoint) {
+ child.offset = length
+ node.__anchor = child
+ }
- // if child is a decorator point, store it as partial decorator
- if (child instanceof DecoratorPoint) {
- node.__decorations = (node.__decorations || []).concat([
- child.withPosition(length),
- ])
+ if (child instanceof FocusPoint || child instanceof CursorPoint) {
+ child.offset = length
+ node.__focus = child
+ }
+
+ if (child instanceof DecorationPoint) {
+ child.offset = length
+ node.__decorations = node.__decorations || []
+ node.__decorations = node.__decorations.concat(child)
}
})
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-block.js b/packages/slate-hyperscript/test/fixtures/cursor-across-block.js
index cd51c91f2..3fbb94bf6 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-block.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-block.js
@@ -48,12 +48,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 1,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 3,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-and-inlines.js b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-and-inlines.js
index 488a3ae85..4cc4e8b49 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-and-inlines.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-and-inlines.js
@@ -137,12 +137,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 1, 0],
- anchorOffset: 2,
- focusKey: '4',
- focusPath: [1, 1, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 2,
+ },
+ focus: {
+ object: 'point',
+ key: '4',
+ path: [1, 1, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-end.js b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-end.js
index 137a95fb7..d5e0624f7 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-end.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-end.js
@@ -71,12 +71,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 3,
- focusKey: '2',
- focusPath: [1, 0],
- focusOffset: 3,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '2',
+ path: [1, 0],
+ offset: 3,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-middle.js b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-middle.js
index fb41fb2a4..57e66af39 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-middle.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-middle.js
@@ -71,12 +71,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 2,
- focusKey: '2',
- focusPath: [1, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 2,
+ },
+ focus: {
+ object: 'point',
+ key: '2',
+ path: [1, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-start.js b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-start.js
index 816f229c1..e417997fd 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-start.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-blocks-start.js
@@ -71,12 +71,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 0,
- focusKey: '2',
- focusPath: [1, 0],
- focusOffset: 0,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ key: '2',
+ path: [1, 0],
+ offset: 0,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-end.js b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-end.js
index f4f42456c..a5f3acb3c 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-end.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-end.js
@@ -92,12 +92,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 3,
- focusKey: '4',
- focusPath: [2, 0],
- focusOffset: 5,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '4',
+ path: [2, 0],
+ offset: 5,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-middle.js b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-middle.js
index a1e4c86fb..45f2b44ce 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-middle.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-middle.js
@@ -92,12 +92,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 2,
- focusKey: '4',
- focusPath: [2, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 2,
+ },
+ focus: {
+ object: 'point',
+ key: '4',
+ path: [2, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-start.js b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-start.js
index 3a033a7fe..e7cbe554b 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-start.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-across-multiple-blocks-start.js
@@ -92,12 +92,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 0,
- focusKey: '4',
- focusPath: [2, 0],
- focusOffset: 0,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ key: '4',
+ path: [2, 0],
+ offset: 0,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-block-end.js b/packages/slate-hyperscript/test/fixtures/cursor-block-end.js
index be2f2b70f..74107c88e 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-block-end.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-block-end.js
@@ -48,12 +48,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 3,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 3,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-block-middle.js b/packages/slate-hyperscript/test/fixtures/cursor-block-middle.js
index 40710006e..324609fd7 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-block-middle.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-block-middle.js
@@ -48,12 +48,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 1,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-block-start.js b/packages/slate-hyperscript/test/fixtures/cursor-block-start.js
index c43df09c0..87a835b7b 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-block-start.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-block-start.js
@@ -48,12 +48,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 0,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 0,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-custom-block-middle.js b/packages/slate-hyperscript/test/fixtures/cursor-custom-block-middle.js
index de782d896..d731d1b50 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-custom-block-middle.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-custom-block-middle.js
@@ -54,12 +54,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 1,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-inline-end.js b/packages/slate-hyperscript/test/fixtures/cursor-inline-end.js
new file mode 100644
index 000000000..9da1cc1b4
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/cursor-inline-end.js
@@ -0,0 +1,102 @@
+/** @jsx h */
+
+import h from '../..'
+
+export const input = (
+
+
+
+ one
+
+ two
+
+ three
+
+
+
+)
+
+export const options = {
+ preserveSelection: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ data: {},
+ key: '6',
+ nodes: [
+ {
+ object: 'block',
+ key: '4',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '2',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ ],
+ },
+ {
+ object: 'inline',
+ key: '1',
+ type: 'link',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ object: 'text',
+ key: '3',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'three',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ selection: {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 3,
+ },
+ isFocused: true,
+ isAtomic: false,
+ marks: null,
+ },
+}
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-inline-start.js b/packages/slate-hyperscript/test/fixtures/cursor-inline-start.js
new file mode 100644
index 000000000..76187afd5
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/cursor-inline-start.js
@@ -0,0 +1,102 @@
+/** @jsx h */
+
+import h from '../..'
+
+export const input = (
+
+
+
+ one
+
+ two
+
+ three
+
+
+
+)
+
+export const options = {
+ preserveSelection: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ data: {},
+ key: '6',
+ nodes: [
+ {
+ object: 'block',
+ key: '4',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '2',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ ],
+ },
+ {
+ object: 'inline',
+ key: '1',
+ type: 'link',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ object: 'text',
+ key: '3',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'three',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ selection: {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 0,
+ },
+ isFocused: true,
+ isAtomic: false,
+ marks: null,
+ },
+}
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-inline.js b/packages/slate-hyperscript/test/fixtures/cursor-inline.js
index bc8d3c76d..549bf30c3 100644
--- a/packages/slate-hyperscript/test/fixtures/cursor-inline.js
+++ b/packages/slate-hyperscript/test/fixtures/cursor-inline.js
@@ -83,12 +83,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 1, 0],
- anchorOffset: 1,
- focusKey: '0',
- focusPath: [0, 1, 0],
- focusOffset: 1,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 1, 0],
+ offset: 1,
+ },
isFocused: true,
isAtomic: false,
marks: null,
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-mark-end.js b/packages/slate-hyperscript/test/fixtures/cursor-mark-end.js
new file mode 100644
index 000000000..4371fd7bf
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/cursor-mark-end.js
@@ -0,0 +1,87 @@
+/** @jsx h */
+
+import h from '../..'
+
+export const input = (
+
+
+
+ one
+
+ two
+
+ three
+
+
+
+)
+
+export const options = {
+ preserveSelection: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ data: {},
+ key: '3',
+ nodes: [
+ {
+ object: 'block',
+ key: '1',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [
+ {
+ object: 'mark',
+ type: 'bold',
+ data: {},
+ },
+ ],
+ },
+ {
+ object: 'leaf',
+ text: 'three',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ selection: {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 6,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 6,
+ },
+ isFocused: true,
+ isAtomic: false,
+ marks: null,
+ },
+}
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-mark-start.js b/packages/slate-hyperscript/test/fixtures/cursor-mark-start.js
new file mode 100644
index 000000000..ed669a85b
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/cursor-mark-start.js
@@ -0,0 +1,87 @@
+/** @jsx h */
+
+import h from '../..'
+
+export const input = (
+
+
+
+ one
+
+ two
+
+ three
+
+
+
+)
+
+export const options = {
+ preserveSelection: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ data: {},
+ key: '3',
+ nodes: [
+ {
+ object: 'block',
+ key: '1',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [
+ {
+ object: 'mark',
+ type: 'bold',
+ data: {},
+ },
+ ],
+ },
+ {
+ object: 'leaf',
+ text: 'three',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ selection: {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ isFocused: true,
+ isAtomic: false,
+ marks: null,
+ },
+}
diff --git a/packages/slate-hyperscript/test/fixtures/cursor-mark.js b/packages/slate-hyperscript/test/fixtures/cursor-mark.js
new file mode 100644
index 000000000..4d12329c6
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/cursor-mark.js
@@ -0,0 +1,87 @@
+/** @jsx h */
+
+import h from '../..'
+
+export const input = (
+
+
+
+ one
+
+ two
+
+ three
+
+
+
+)
+
+export const options = {
+ preserveSelection: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ data: {},
+ key: '3',
+ nodes: [
+ {
+ object: 'block',
+ key: '1',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [
+ {
+ object: 'mark',
+ type: 'bold',
+ data: {},
+ },
+ ],
+ },
+ {
+ object: 'leaf',
+ text: 'three',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ selection: {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 4,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 4,
+ },
+ isFocused: true,
+ isAtomic: false,
+ marks: null,
+ },
+}
diff --git a/packages/slate-hyperscript/test/fixtures/decoration-across-blocks.js b/packages/slate-hyperscript/test/fixtures/decoration-across-blocks.js
new file mode 100644
index 000000000..91db9959d
--- /dev/null
+++ b/packages/slate-hyperscript/test/fixtures/decoration-across-blocks.js
@@ -0,0 +1,107 @@
+/** @jsx h */
+
+import { createHyperscript } from '../..'
+
+const h = createHyperscript({
+ blocks: {
+ paragraph: 'paragraph',
+ },
+ decorators: {
+ highlight: 'highlight',
+ },
+})
+
+export const input = (
+
+
+
+ one
+
+
+ two
+
+
+
+)
+
+export const options = {
+ preserveDecorations: true,
+ preserveKeys: true,
+}
+
+export const output = {
+ object: 'value',
+ document: {
+ object: 'document',
+ key: '6',
+ data: {},
+ nodes: [
+ {
+ object: 'block',
+ key: '1',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '0',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'one',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ object: 'block',
+ key: '3',
+ type: 'paragraph',
+ isVoid: false,
+ data: {},
+ nodes: [
+ {
+ object: 'text',
+ key: '2',
+ leaves: [
+ {
+ object: 'leaf',
+ text: 'two',
+ marks: [],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ decorations: [
+ {
+ object: 'range',
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: '2',
+ path: [1, 0],
+ offset: 2,
+ },
+ isFocused: false,
+ isAtomic: false,
+ marks: [
+ {
+ object: 'mark',
+ type: 'highlight',
+ data: {},
+ },
+ ],
+ },
+ ],
+}
diff --git a/packages/slate-hyperscript/test/fixtures/decoration.js b/packages/slate-hyperscript/test/fixtures/decoration.js
index d57fc9bcb..9d2d3964b 100644
--- a/packages/slate-hyperscript/test/fixtures/decoration.js
+++ b/packages/slate-hyperscript/test/fixtures/decoration.js
@@ -58,12 +58,18 @@ export const output = {
decorations: [
{
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 3,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 6,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 3,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 6,
+ },
isFocused: false,
isAtomic: false,
marks: [
diff --git a/packages/slate-hyperscript/test/fixtures/selection.js b/packages/slate-hyperscript/test/fixtures/selection.js
index f4698b111..bbec5c64f 100644
--- a/packages/slate-hyperscript/test/fixtures/selection.js
+++ b/packages/slate-hyperscript/test/fixtures/selection.js
@@ -9,7 +9,10 @@ export const input = (
onetwothree
-
+
+
+
+
)
@@ -49,12 +52,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: 'a',
- anchorPath: [0, 0],
- anchorOffset: 1,
- focusKey: 'a',
- focusPath: [0, 0],
- focusOffset: 2,
+ anchor: {
+ object: 'point',
+ key: 'a',
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ object: 'point',
+ key: 'a',
+ path: [0, 0],
+ offset: 2,
+ },
isFocused: false,
isAtomic: false,
marks: null,
diff --git a/packages/slate-react/src/components/content.js b/packages/slate-react/src/components/content.js
index eb4ac6572..f250d27f8 100644
--- a/packages/slate-react/src/components/content.js
+++ b/packages/slate-react/src/components/content.js
@@ -376,7 +376,7 @@ class Content extends React.Component {
editor.change(change => {
if (change.value.isInVoid) {
- change.collapseToStartOfNextText()
+ change.moveToStartOfNextText()
} else {
change.splitBlockAtRange(range)
}
diff --git a/packages/slate-react/src/components/text.js b/packages/slate-react/src/components/text.js
index 4e9637bdb..7790d116c 100644
--- a/packages/slate-react/src/components/text.js
+++ b/packages/slate-react/src/components/text.js
@@ -109,20 +109,20 @@ class Text extends React.Component {
const { key } = node
const decs = decorations.filter(d => {
- const { startKey, endKey, startPath, endPath } = d
+ const { start, end } = d
// If either of the decoration's keys match, include it.
- if (startKey === key || endKey === key) return true
+ if (start.key === key || end.key === key) return true
// Otherwise, if the decoration is in a single node, it's not ours.
- if (startKey === endKey) return false
+ if (start.key === end.key) return false
// If the node's path is before the start path, ignore it.
const path = document.assertPathByKey(key)
- if (PathUtils.compare(path, startPath) === -1) return false
+ if (PathUtils.compare(path, start.path) === -1) return false
// If the node's path is after the end path, ignore it.
- if (PathUtils.compare(path, endPath) === 1) return false
+ if (PathUtils.compare(path, end.path) === 1) return false
// Otherwise, include it.
return true
diff --git a/packages/slate-react/src/plugins/after.js b/packages/slate-react/src/plugins/after.js
index 9b81f6342..c17c99b9d 100644
--- a/packages/slate-react/src/plugins/after.js
+++ b/packages/slate-react/src/plugins/after.js
@@ -85,7 +85,7 @@ function AfterPlugin() {
// an inline node will be automatically replaced to be at the last offset
// of a previous inline node, which screws us up, so we always want to set
// it to the end of the node. (2016/11/29)
- change.focus().collapseToEndOf(node)
+ change.focus().moveToEndOfNode(node)
}
debug('onClick', { event })
@@ -125,7 +125,8 @@ function AfterPlugin() {
// If user cuts a void block node or a void inline node,
// manually removes it since selection is collapsed in this case.
const { value } = change
- const { endBlock, endInline, isCollapsed } = value
+ const { endBlock, endInline, selection } = value
+ const { isCollapsed } = selection
const isVoidBlock = endBlock && endBlock.isVoid && isCollapsed
const isVoidInline = endInline && endInline.isVoid && isCollapsed
@@ -219,13 +220,13 @@ function AfterPlugin() {
// needs to account for the selection's content being deleted.
if (
isDraggingInternally &&
- selection.endKey == target.endKey &&
- selection.endOffset < target.endOffset
+ selection.end.key == target.end.key &&
+ selection.end.offset < target.end.offset
) {
target = target.move(
- selection.startKey == selection.endKey
- ? 0 - selection.endOffset + selection.startOffset
- : 0 - selection.endOffset
+ selection.start.key == selection.end.key
+ ? 0 - selection.end.offset + selection.start.offset
+ : 0 - selection.end.offset
)
}
@@ -236,11 +237,11 @@ function AfterPlugin() {
change.select(target)
if (type == 'text' || type == 'html') {
- const { anchorKey } = target
- let hasVoidParent = document.hasVoidParent(anchorKey)
+ const { anchor } = target
+ let hasVoidParent = document.hasVoidParent(anchor.key)
if (hasVoidParent) {
- let n = document.getNode(anchorKey)
+ let n = document.getNode(anchor.key)
while (hasVoidParent) {
n = document.getNextText(n.key)
@@ -248,7 +249,7 @@ function AfterPlugin() {
hasVoidParent = document.hasVoidParent(n.key)
}
- if (n) change.collapseToStartOf(n)
+ if (n) change.moveToStartOfNode(n)
}
if (text) {
@@ -275,7 +276,7 @@ function AfterPlugin() {
// has fired in a node: https://github.com/facebook/react/issues/11379.
// Until this is fixed in React, we dispatch a mouseup event on that
// DOM node, since that will make it go back to normal.
- const focusNode = document.getNode(target.focusKey)
+ const focusNode = document.getNode(target.focus.key)
const el = findDOMNode(focusNode, window)
if (!el) return
@@ -343,12 +344,12 @@ function AfterPlugin() {
// Determine what the selection should be after changing the text.
const delta = textContent.length - text.length
- const corrected = selection.collapseToEnd().move(delta)
+ const corrected = selection.moveToEnd().moveForward(delta)
let entire = selection
.moveAnchorTo(point.key, start)
.moveFocusTo(point.key, end)
- entire = document.normalizeRange(entire)
+ entire = document.resolveRange(entire)
// Change the current value to have the leaf's text replaced.
change.insertTextAtRange(entire, textContent, leaf.marks).select(corrected)
@@ -372,7 +373,7 @@ function AfterPlugin() {
// preserve native autocorrect behavior, so they shouldn't be handled here.
if (Hotkeys.isSplitBlock(event) && !IS_IOS) {
return value.isInVoid
- ? change.collapseToStartOfNextText()
+ ? change.moveToStartOfNextText()
: change.splitBlock()
}
@@ -413,22 +414,22 @@ function AfterPlugin() {
// selection isn't properly collapsed. (2017/10/17)
if (Hotkeys.isCollapseLineBackward(event)) {
event.preventDefault()
- return change.collapseLineBackward()
+ return change.moveToStartOfBlock()
}
if (Hotkeys.isCollapseLineForward(event)) {
event.preventDefault()
- return change.collapseLineForward()
+ return change.moveToEndOfBlock()
}
if (Hotkeys.isExtendLineBackward(event)) {
event.preventDefault()
- return change.extendLineBackward()
+ return change.moveFocusToStartOfBlock()
}
if (Hotkeys.isExtendLineForward(event)) {
event.preventDefault()
- return change.extendLineForward()
+ return change.moveFocusToEndOfBlock()
}
// COMPAT: If a void node is selected, or a zero-width text node adjacent to
@@ -441,7 +442,7 @@ function AfterPlugin() {
if (isInVoid || isPreviousInVoid || startText.text == '') {
event.preventDefault()
- return change.collapseCharBackward()
+ return change.moveBackward()
}
}
@@ -451,7 +452,7 @@ function AfterPlugin() {
if (isInVoid || isNextInVoid || startText.text == '') {
event.preventDefault()
- return change.collapseCharForward()
+ return change.moveForward()
}
}
@@ -462,7 +463,7 @@ function AfterPlugin() {
if (isInVoid || isPreviousInVoid || startText.text == '') {
event.preventDefault()
- return change.extendCharBackward()
+ return change.moveFocusBackward()
}
}
@@ -472,7 +473,7 @@ function AfterPlugin() {
if (isInVoid || isNextInVoid || startText.text == '') {
event.preventDefault()
- return change.extendCharForward()
+ return change.moveFocusForward()
}
}
}
@@ -535,13 +536,13 @@ function AfterPlugin() {
let range = findRange(native, value)
if (!range) return
- const { anchorKey, anchorOffset, focusKey, focusOffset } = range
- const anchorText = document.getNode(anchorKey)
- const focusText = document.getNode(focusKey)
- const anchorInline = document.getClosestInline(anchorKey)
- const focusInline = document.getClosestInline(focusKey)
- const focusBlock = document.getClosestBlock(focusKey)
- const anchorBlock = document.getClosestBlock(anchorKey)
+ const { anchor, focus } = range
+ const anchorText = document.getNode(anchor.key)
+ const focusText = document.getNode(focus.key)
+ const anchorInline = document.getClosestInline(anchor.key)
+ const focusInline = document.getClosestInline(focus.key)
+ const focusBlock = document.getClosestBlock(focus.key)
+ const anchorBlock = document.getClosestBlock(anchor.key)
// COMPAT: If the anchor point is at the start of a non-void, and the
// focus point is inside a void node with an offset that isn't `0`, set
@@ -553,12 +554,12 @@ function AfterPlugin() {
if (
anchorBlock &&
!anchorBlock.isVoid &&
- anchorOffset == 0 &&
+ anchor.offset == 0 &&
focusBlock &&
focusBlock.isVoid &&
- focusOffset != 0
+ focus.offset != 0
) {
- range = range.set('focusOffset', 0)
+ range = range.setFocus(focus.setOffset(0))
}
// COMPAT: If the selection is at the end of a non-void inline node, and
@@ -567,24 +568,24 @@ function AfterPlugin() {
if (
anchorInline &&
!anchorInline.isVoid &&
- anchorOffset == anchorText.text.length
+ anchor.offset == anchorText.text.length
) {
- const block = document.getClosestBlock(anchorKey)
- const next = block.getNextText(anchorKey)
+ const block = document.getClosestBlock(anchor.key)
+ const next = block.getNextText(anchor.key)
if (next) range = range.moveAnchorTo(next.key, 0)
}
if (
focusInline &&
!focusInline.isVoid &&
- focusOffset == focusText.text.length
+ focus.offset == focusText.text.length
) {
- const block = document.getClosestBlock(focusKey)
- const next = block.getNextText(focusKey)
+ const block = document.getClosestBlock(focus.key)
+ const next = block.getNextText(focus.key)
if (next) range = range.moveFocusTo(next.key, 0)
}
- range = document.normalizeRange(range)
+ range = document.resolveRange(range)
change.select(range)
}
diff --git a/packages/slate-react/src/utils/clone-fragment.js b/packages/slate-react/src/utils/clone-fragment.js
index 27a2d282c..03fc194ac 100644
--- a/packages/slate-react/src/utils/clone-fragment.js
+++ b/packages/slate-react/src/utils/clone-fragment.js
@@ -19,9 +19,9 @@ const { FRAGMENT, HTML, TEXT } = TRANSFER_TYPES
function cloneFragment(event, value, fragment = value.fragment) {
const window = getWindow(event.target)
const native = window.getSelection()
- const { startKey, endKey } = value
- const startVoid = value.document.getClosestVoid(startKey)
- const endVoid = value.document.getClosestVoid(endKey)
+ const { start, end } = value.selection
+ const startVoid = value.document.getClosestVoid(start.key)
+ const endVoid = value.document.getClosestVoid(end.key)
// If the selection is collapsed, and it isn't inside a void node, abort.
if (native.isCollapsed && !startVoid) return
diff --git a/packages/slate-react/src/utils/find-dom-point.js b/packages/slate-react/src/utils/find-dom-point.js
index 789508b58..34686ab76 100644
--- a/packages/slate-react/src/utils/find-dom-point.js
+++ b/packages/slate-react/src/utils/find-dom-point.js
@@ -1,16 +1,15 @@
import findDOMNode from './find-dom-node'
/**
- * Find a native DOM selection point from a Slate `key` and `offset`.
+ * Find a native DOM selection point from a Slate `point`.
*
- * @param {String} key
- * @param {Number} offset
+ * @param {Point} point
* @param {Window} win (optional)
* @return {Object|Null}
*/
-function findDOMPoint(key, offset, win = window) {
- const el = findDOMNode(key, win)
+function findDOMPoint(point, win = window) {
+ const el = findDOMNode(point.key, win)
let start = 0
let n
@@ -27,8 +26,8 @@ function findDOMPoint(key, offset, win = window) {
const { length } = n.textContent
const end = start + length
- if (offset <= end) {
- const o = offset - start
+ if (point.offset <= end) {
+ const o = point.offset - start
return { node: n, offset: o >= 0 ? o : 0 }
}
diff --git a/packages/slate-react/src/utils/find-dom-range.js b/packages/slate-react/src/utils/find-dom-range.js
index d1c753f11..33a5b0f25 100644
--- a/packages/slate-react/src/utils/find-dom-range.js
+++ b/packages/slate-react/src/utils/find-dom-range.js
@@ -9,21 +9,15 @@ import findDOMPoint from './find-dom-point'
*/
function findDOMRange(range, win = window) {
- const {
- anchorKey,
- anchorOffset,
- focusKey,
- focusOffset,
- isBackward,
- isCollapsed,
- } = range
- const anchor = findDOMPoint(anchorKey, anchorOffset, win)
- const focus = isCollapsed ? anchor : findDOMPoint(focusKey, focusOffset, win)
- if (!anchor || !focus) return null
+ const { anchor, focus, isBackward, isCollapsed } = range
+ const domAnchor = findDOMPoint(anchor, win)
+ const domFocus = isCollapsed ? domAnchor : findDOMPoint(focus, win)
+
+ if (!domAnchor || !domFocus) return null
const r = win.document.createRange()
- const start = isBackward ? focus : anchor
- const end = isBackward ? anchor : focus
+ const start = isBackward ? domFocus : domAnchor
+ const end = isBackward ? domAnchor : domFocus
r.setStart(start.node, start.offset)
r.setEnd(end.node, end.offset)
return r
diff --git a/packages/slate-react/src/utils/find-point.js b/packages/slate-react/src/utils/find-point.js
index 75a27cf8a..5439ddd32 100644
--- a/packages/slate-react/src/utils/find-point.js
+++ b/packages/slate-react/src/utils/find-point.js
@@ -21,7 +21,7 @@ const VOID_SELECTOR = '[data-slate-void]'
* @param {Element} nativeNode
* @param {Number} nativeOffset
* @param {Value} value
- * @return {Object}
+ * @return {Point}
*/
function findPoint(nativeNode, nativeOffset, value) {
@@ -78,10 +78,8 @@ function findPoint(nativeNode, nativeOffset, value) {
// then afterwards for the correct `element`. (2017/03/03)
if (!value.document.hasDescendant(key)) return null
- return {
- key,
- offset,
- }
+ const point = value.document.createPoint({ key, offset })
+ return point
}
/**
diff --git a/packages/slate-react/src/utils/find-range.js b/packages/slate-react/src/utils/find-range.js
index 07fa3dd1f..1928d156a 100644
--- a/packages/slate-react/src/utils/find-range.js
+++ b/packages/slate-react/src/utils/find-range.js
@@ -48,8 +48,8 @@ function findRange(native, value) {
// last word of a span, it sets the endContainer to the containing span.
// `selection-is-backward` doesn't handle this case.
if (IS_IE || IS_EDGE) {
- const domAnchor = findDOMPoint(anchor.key, anchor.offset)
- const domFocus = findDOMPoint(focus.key, focus.offset)
+ const domAnchor = findDOMPoint(anchor)
+ const domFocus = findDOMPoint(focus)
native = {
anchorNode: domAnchor.node,
@@ -61,10 +61,8 @@ function findRange(native, value) {
const { document } = value
const range = document.createRange({
- anchorKey: anchor.key,
- anchorOffset: anchor.offset,
- focusKey: focus.key,
- focusOffset: focus.offset,
+ anchor,
+ focus,
isBackward: isCollapsed ? false : isBackward(native),
isFocused: true,
})
diff --git a/packages/slate-react/src/utils/get-children-decorations.js b/packages/slate-react/src/utils/get-children-decorations.js
index bb3ac48ad..85970bdc0 100644
--- a/packages/slate-react/src/utils/get-children-decorations.js
+++ b/packages/slate-react/src/utils/get-children-decorations.js
@@ -80,7 +80,7 @@ function orderChildDecorations(node, decorations) {
// Range start.
// A rangeStart should be before the child containing its startKey, in order
// to consider it active before going down the child.
- const startKeyOrder = keyOrders[decoration.startKey]
+ const startKeyOrder = keyOrders[decoration.start.key]
const containingChildOrder =
startKeyOrder === undefined
? 0
@@ -93,7 +93,7 @@ function orderChildDecorations(node, decorations) {
})
// Range end.
- const endKeyOrder = (keyOrders[decoration.endKey] || globalOrder) + 0.5
+ const endKeyOrder = (keyOrders[decoration.end.key] || globalOrder) + 0.5
endPoints.push({
isRangeEnd: true,
diff --git a/packages/slate-react/test/rendering/fixtures/custom-decorator.js b/packages/slate-react/test/rendering/fixtures/custom-decorator.js
index 467eabae1..9dd78ad82 100644
--- a/packages/slate-react/test/rendering/fixtures/custom-decorator.js
+++ b/packages/slate-react/test/rendering/fixtures/custom-decorator.js
@@ -7,10 +7,14 @@ function decorateNode(block) {
const text = block.getFirstText()
return [
{
- anchorKey: text.key,
- anchorOffset: 1,
- focusKey: text.key,
- focusOffset: 2,
+ anchor: {
+ key: text.key,
+ offset: 1,
+ },
+ focus: {
+ key: text.key,
+ offset: 2,
+ },
marks: [{ type: 'bold' }],
},
]
diff --git a/packages/slate/src/changes/at-current-range.js b/packages/slate/src/changes/at-current-range.js
index 709be1983..77cbd628f 100644
--- a/packages/slate/src/changes/at-current-range.js
+++ b/packages/slate/src/changes/at-current-range.js
@@ -42,9 +42,9 @@ PROXY_TRANSFORMS.forEach(method => {
change[methodAtRange](selection, ...args)
if (method.match(/Backward$/)) {
- change.collapseToStart()
+ change.moveToStart()
} else if (method.match(/Forward$/)) {
- change.collapseToEnd()
+ change.moveToEnd()
}
}
})
@@ -117,7 +117,7 @@ Changes.delete = change => {
// Ensure that the selection is collapsed to the start, because in certain
// cases when deleting across inline nodes, when splitting the inline node the
// end point of the selection will end up after the split point.
- change.collapseToStart()
+ change.moveToStart()
}
/**
@@ -135,7 +135,7 @@ Changes.insertBlock = (change, block) => {
// If the node was successfully inserted, update the selection.
const node = change.value.document.getNode(block.key)
- if (node) change.collapseToEndOf(node)
+ if (node) change.moveToEndOfNode(node)
}
/**
@@ -150,6 +150,7 @@ Changes.insertFragment = (change, fragment) => {
let { value } = change
let { document, selection } = value
+ const { start, end } = selection
const { startText, endText, startInline } = value
const lastText = fragment.getLastText()
const lastInline = fragment.getClosestInline(lastText.key)
@@ -158,8 +159,8 @@ Changes.insertFragment = (change, fragment) => {
const keys = document.getTexts().map(text => text.key)
const isAppending =
!startInline ||
- selection.hasEdgeAtStartOf(startText) ||
- selection.hasEdgeAtEndOf(endText)
+ (start.isAtStartOfNode(startText) || end.isAtStartOfNode(startText)) ||
+ (start.isAtEndOfNode(endText) || end.isAtEndOfNode(endText))
const isInserting =
firstChild.hasBlockChildren() || lastChild.hasBlockChildren()
@@ -172,13 +173,13 @@ Changes.insertFragment = (change, fragment) => {
const newText = isAppending ? newTexts.last() : newTexts.takeLast(2).first()
if (newText && (lastInline || isInserting)) {
- change.select(selection.collapseToEndOf(newText))
+ change.select(selection.moveToEndOfNode(newText))
} else if (newText) {
change.select(
- selection.collapseToStartOf(newText).move(lastText.text.length)
+ selection.moveToStartOfNode(newText).moveForward(lastText.text.length)
)
} else {
- change.select(selection.collapseToStart().move(lastText.text.length))
+ change.select(selection.moveToStart().moveForward(lastText.text.length))
}
}
@@ -197,7 +198,7 @@ Changes.insertInline = (change, inline) => {
// If the node was successfully inserted, update the selection.
const node = change.value.document.getNode(inline.key)
- if (node) change.collapseToEndOf(node)
+ if (node) change.moveToEndOfNode(node)
}
/**
@@ -232,7 +233,7 @@ Changes.splitBlock = (change, depth = 1) => {
const { value } = change
const { selection, document } = value
const marks = selection.marks || document.getInsertMarksAtRange(selection)
- change.splitBlockAtRange(selection, depth).collapseToEnd()
+ change.splitBlockAtRange(selection, depth).moveToEnd()
if (marks && marks.size !== 0) {
change.select({ marks })
@@ -312,12 +313,12 @@ Changes.wrapText = (change, prefix, suffix = prefix) => {
// If the selection was collapsed, it will have moved the start offset too.
if (selection.isCollapsed) {
- change.moveStart(0 - prefix.length)
+ change.moveStartBackward(prefix.length)
}
// Adding the suffix will have pushed the end of the selection further on, so
// we need to move it back to account for this.
- change.moveEnd(0 - suffix.length)
+ change.moveEndBackward(suffix.length)
// There's a chance that the selection points moved "through" each other,
// resulting in a now-incorrect selection direction.
diff --git a/packages/slate/src/changes/at-range.js b/packages/slate/src/changes/at-range.js
index 5522e0112..f47796240 100644
--- a/packages/slate/src/changes/at-range.js
+++ b/packages/slate/src/changes/at-range.js
@@ -30,7 +30,7 @@ Changes.addMarkAtRange = (change, range, mark, options = {}) => {
const normalize = change.getFlag('normalize', options)
const { value } = change
const { document } = value
- const { startKey, startOffset, endKey, endOffset } = range
+ const { start, end } = range
const texts = document.getTextsAtRange(range)
texts.forEach(node => {
@@ -38,9 +38,9 @@ Changes.addMarkAtRange = (change, range, mark, options = {}) => {
let index = 0
let length = node.text.length
- if (key == startKey) index = startOffset
- if (key == endKey) length = endOffset
- if (key == startKey && key == endKey) length = endOffset - startOffset
+ if (key == start.key) index = start.offset
+ if (key == end.key) length = end.offset
+ if (key == start.key && key == end.key) length = end.offset - start.offset
change.addMarkByKey(key, index, length, mark, { normalize })
})
@@ -78,7 +78,11 @@ Changes.deleteAtRange = (change, range, options = {}) => {
const normalize = change.getFlag('normalize', options)
const { value } = change
- let { startKey, startOffset, endKey, endOffset } = range
+ const { start, end } = range
+ let startKey = start.key
+ let startOffset = start.offset
+ let endKey = end.key
+ let endOffset = end.offset
let { document } = value
let isStartVoid = document.hasVoidParent(startKey)
let isEndVoid = document.hasVoidParent(endKey)
@@ -273,10 +277,10 @@ Changes.deleteAtRange = (change, range, options = {}) => {
Changes.deleteCharBackwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
const { text } = startBlock
const n = TextUtils.getCharOffsetBackward(text, o)
change.deleteBackwardAtRange(range, n, options)
@@ -294,10 +298,10 @@ Changes.deleteCharBackwardAtRange = (change, range, options) => {
Changes.deleteLineBackwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
change.deleteBackwardAtRange(range, o, options)
}
@@ -313,10 +317,10 @@ Changes.deleteLineBackwardAtRange = (change, range, options) => {
Changes.deleteWordBackwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
const { text } = startBlock
const n = TextUtils.getWordOffsetBackward(text, o)
change.deleteBackwardAtRange(range, n, options)
@@ -337,7 +341,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
const normalize = change.getFlag('normalize', options)
const { value } = change
const { document } = value
- const { startKey, focusOffset } = range
+ const { start, focus } = range
// If the range is expanded, perform a regular delete instead.
if (range.isExpanded) {
@@ -345,7 +349,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
return
}
- const voidParent = document.getClosestVoid(startKey)
+ const voidParent = document.getClosestVoid(start.key)
// If there is a void parent, delete it.
if (voidParent) {
@@ -353,7 +357,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
return
}
- const block = document.getClosestBlock(startKey)
+ const block = document.getClosestBlock(start.key)
// If the closest is not void, but empty, remove it
if (block && block.isEmpty && document.nodes.size !== 1) {
@@ -362,15 +366,15 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
}
// If the range is at the start of the document, abort.
- if (range.isAtStartOf(document)) {
+ if (start.isAtStartOfNode(document)) {
return
}
// If the range is at the start of the text node, we need to figure out what
// is behind it to know how to delete...
- const text = document.getDescendant(startKey)
+ const text = document.getDescendant(start.key)
- if (range.isAtStartOf(text)) {
+ if (start.isAtStartOfNode(text)) {
const prev = document.getPreviousText(text.key)
const prevBlock = document.getClosestBlock(prev.key)
const prevVoid = document.getClosestVoid(prev.key)
@@ -384,11 +388,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
// If we're deleting by one character and the previous text node is not
// inside the current block, we need to merge the two blocks together.
if (n == 1 && prevBlock != block) {
- range = range.merge({
- anchorKey: prev.key,
- anchorOffset: prev.text.length,
- })
-
+ range = range.moveAnchorTo(prev.key, prev.text.length)
change.deleteAtRange(range, { normalize })
return
}
@@ -396,8 +396,8 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
// If the focus offset is farther than the number of characters to delete,
// just remove the characters backwards inside the current node.
- if (n < focusOffset) {
- range = range.merge({ focusOffset: focusOffset - n })
+ if (n < focus.offset) {
+ range = range.moveFocusBackward(n)
change.deleteAtRange(range, { normalize })
return
}
@@ -405,7 +405,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
// Otherwise, we need to see how many nodes backwards to go.
let node = text
let offset = 0
- let traversed = focusOffset
+ let traversed = focus.offset
while (n > traversed) {
node = document.getPreviousText(node.key)
@@ -419,11 +419,7 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
}
}
- range = range.merge({
- anchorKey: node.key,
- anchorOffset: offset,
- })
-
+ range = range.moveAnchorTo(node.key, offset)
change.deleteAtRange(range, { normalize })
}
@@ -439,10 +435,10 @@ Changes.deleteBackwardAtRange = (change, range, n = 1, options = {}) => {
Changes.deleteCharForwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
const { text } = startBlock
const n = TextUtils.getCharOffsetForward(text, o)
change.deleteForwardAtRange(range, n, options)
@@ -460,10 +456,10 @@ Changes.deleteCharForwardAtRange = (change, range, options) => {
Changes.deleteLineForwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
change.deleteForwardAtRange(range, startBlock.text.length - o, options)
}
@@ -479,10 +475,10 @@ Changes.deleteLineForwardAtRange = (change, range, options) => {
Changes.deleteWordForwardAtRange = (change, range, options) => {
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const startBlock = document.getClosestBlock(startKey)
- const offset = startBlock.getOffset(startKey)
- const o = offset + startOffset
+ const { start } = range
+ const startBlock = document.getClosestBlock(start.key)
+ const offset = startBlock.getOffset(start.key)
+ const o = offset + start.offset
const { text } = startBlock
const n = TextUtils.getWordOffsetForward(text, o)
change.deleteForwardAtRange(range, n, options)
@@ -503,7 +499,7 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
const normalize = change.getFlag('normalize', options)
const { value } = change
const { document } = value
- const { startKey, focusOffset } = range
+ const { start, focus } = range
// If the range is expanded, perform a regular delete instead.
if (range.isExpanded) {
@@ -511,7 +507,7 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
return
}
- const voidParent = document.getClosestVoid(startKey)
+ const voidParent = document.getClosestVoid(start.key)
// If the node has a void parent, delete it.
if (voidParent) {
@@ -519,7 +515,7 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
return
}
- const block = document.getClosestBlock(startKey)
+ const block = document.getClosestBlock(start.key)
// If the closest is not void, but empty, remove it
if (block && block.isEmpty && document.nodes.size !== 1) {
@@ -527,21 +523,21 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
change.removeNodeByKey(block.key, { normalize })
if (nextBlock && nextBlock.key) {
- change.moveToStartOf(nextBlock)
+ change.moveToStartOfNode(nextBlock)
}
return
}
// If the range is at the start of the document, abort.
- if (range.isAtEndOf(document)) {
+ if (start.isAtEndOfNode(document)) {
return
}
// If the range is at the start of the text node, we need to figure out what
// is behind it to know how to delete...
- const text = document.getDescendant(startKey)
+ const text = document.getDescendant(start.key)
- if (range.isAtEndOf(text)) {
+ if (start.isAtEndOfNode(text)) {
const next = document.getNextText(text.key)
const nextBlock = document.getClosestBlock(next.key)
const nextVoid = document.getClosestVoid(next.key)
@@ -555,11 +551,7 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
// If we're deleting by one character and the previous text node is not
// inside the current block, we need to merge the two blocks together.
if (n == 1 && nextBlock != block) {
- range = range.merge({
- focusKey: next.key,
- focusOffset: 0,
- })
-
+ range = range.moveFocusTo(next.key, 0)
change.deleteAtRange(range, { normalize })
return
}
@@ -568,19 +560,16 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
// If the remaining characters to the end of the node is greater than or equal
// to the number of characters to delete, just remove the characters forwards
// inside the current node.
- if (n <= text.text.length - focusOffset) {
- range = range.merge({
- focusOffset: focusOffset + n,
- })
-
+ if (n <= text.text.length - focus.offset) {
+ range = range.moveFocusForward(n)
change.deleteAtRange(range, { normalize })
return
}
// Otherwise, we need to see how many nodes forwards to go.
let node = text
- let offset = focusOffset
- let traversed = text.text.length - focusOffset
+ let offset = focus.offset
+ let traversed = text.text.length - focus.offset
while (n > traversed) {
node = document.getNextText(node.key)
@@ -594,11 +583,7 @@ Changes.deleteForwardAtRange = (change, range, n = 1, options = {}) => {
}
}
- range = range.merge({
- focusKey: node.key,
- focusOffset: offset,
- })
-
+ range = range.moveFocusTo(node.key, offset)
change.deleteAtRange(range, { normalize })
}
@@ -618,39 +603,41 @@ Changes.insertBlockAtRange = (change, range, block, options = {}) => {
if (range.isExpanded) {
change.deleteAtRange(range)
- range = range.collapseToStart()
+ range = range.moveToStart()
}
const { value } = change
const { document } = value
- let { startKey, startOffset } = range
+ const { start } = range
+ let startKey = start.key
+ let startOffset = start.offset
const startBlock = document.getClosestBlock(startKey)
const startInline = document.getClosestInline(startKey)
const parent = document.getParent(startBlock.key)
const index = parent.nodes.indexOf(startBlock)
if (startBlock.isVoid) {
- const extra = range.isAtEndOf(startBlock) ? 1 : 0
+ const extra = start.isAtEndOfNode(startBlock) ? 1 : 0
change.insertNodeByKey(parent.key, index + extra, block, { normalize })
} else if (startBlock.isEmpty) {
change.insertNodeByKey(parent.key, index + 1, block, { normalize })
- } else if (range.isAtStartOf(startBlock)) {
+ } else if (start.isAtStartOfNode(startBlock)) {
change.insertNodeByKey(parent.key, index, block, { normalize })
- } else if (range.isAtEndOf(startBlock)) {
+ } else if (start.isAtEndOfNode(startBlock)) {
change.insertNodeByKey(parent.key, index + 1, block, { normalize })
} else {
if (startInline && startInline.isVoid) {
- const atEnd = range.isAtEndOf(startInline)
+ const atEnd = start.isAtEndOfNode(startInline)
const siblingText = atEnd
? document.getNextText(startKey)
: document.getPreviousText(startKey)
const splitRange = atEnd
- ? range.moveToStartOf(siblingText)
- : range.moveToEndOf(siblingText)
+ ? range.moveToStartOfNode(siblingText)
+ : range.moveToEndOfNode(siblingText)
- startKey = splitRange.startKey
- startOffset = splitRange.startOffset
+ startKey = splitRange.start.key
+ startOffset = splitRange.start.offset
}
change.splitDescendantsByKey(startBlock.key, startKey, startOffset, {
@@ -682,10 +669,10 @@ Changes.insertFragmentAtRange = (change, range, fragment, options = {}) => {
if (range.isExpanded) {
change.deleteAtRange(range, { normalize: false })
- if (change.value.document.getDescendant(range.startKey)) {
- range = range.collapseToStart()
+ if (change.value.document.getDescendant(range.start.key)) {
+ range = range.moveToStart()
} else {
- range = range.collapseTo(range.endKey, 0)
+ range = range.moveTo(range.end.key, 0).normalize(change.value.document)
}
}
@@ -699,13 +686,13 @@ Changes.insertFragmentAtRange = (change, range, fragment, options = {}) => {
fragment = fragment.mapDescendants(child => child.regenerateKey())
// Calculate a few things...
- const { startKey, startOffset } = range
+ const { start } = range
const { value } = change
let { document } = value
- let startText = document.getDescendant(startKey)
+ let startText = document.getDescendant(start.key)
let startBlock = document.getClosestBlock(startText.key)
let startChild = startBlock.getFurthestAncestor(startText.key)
- const isAtStart = range.isAtStartOf(startBlock)
+ const isAtStart = start.isAtStartOfNode(startBlock)
const parent = document.getParent(startBlock.key)
const index = parent.nodes.indexOf(startBlock)
const blocks = fragment.getBlocks()
@@ -747,16 +734,16 @@ Changes.insertFragmentAtRange = (change, range, fragment, options = {}) => {
}
// Check if we need to split the node.
- if (startOffset != 0) {
- change.splitDescendantsByKey(startChild.key, startKey, startOffset, {
+ if (start.offset != 0) {
+ change.splitDescendantsByKey(startChild.key, start.key, start.offset, {
normalize: false,
})
}
// Update our variables with the new value.
document = change.value.document
- startText = document.getDescendant(startKey)
- startBlock = document.getClosestBlock(startKey)
+ startText = document.getDescendant(start.key)
+ startBlock = document.getClosestBlock(start.key)
startChild = startBlock.getFurthestAncestor(startText.key)
// If the first and last block aren't the same, we need to move any of the
@@ -792,7 +779,7 @@ Changes.insertFragmentAtRange = (change, range, fragment, options = {}) => {
const inlineIndex = startBlock.nodes.indexOf(inlineChild)
firstBlock.nodes.forEach((inline, i) => {
- const o = startOffset == 0 ? 0 : 1
+ const o = start.offset == 0 ? 0 : 1
const newIndex = inlineIndex + i + o
change.insertNodeByKey(startBlock.key, newIndex, inline, {
@@ -823,19 +810,19 @@ Changes.insertInlineAtRange = (change, range, inline, options = {}) => {
if (range.isExpanded) {
change.deleteAtRange(range, { normalize: false })
- range = range.collapseToStart()
+ range = range.moveToStart()
}
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- const parent = document.getParent(startKey)
- const startText = document.assertDescendant(startKey)
+ const { start } = range
+ const parent = document.getParent(start.key)
+ const startText = document.assertDescendant(start.key)
const index = parent.nodes.indexOf(startText)
if (parent.isVoid) return
- change.splitNodeByKey(startKey, startOffset, { normalize: false })
+ change.splitNodeByKey(start.key, start.offset, { normalize: false })
change.insertNodeByKey(parent.key, index + 1, inline, { normalize: false })
if (normalize) {
@@ -858,20 +845,19 @@ Changes.insertTextAtRange = (change, range, text, marks, options = {}) => {
let { normalize } = options
const { value } = change
const { document } = value
- const { startKey, startOffset } = range
- let key = startKey
- let offset = startOffset
- const parent = document.getParent(startKey)
-
+ const { start } = range
+ let key = start.key
+ let offset = start.offset
+ const parent = document.getParent(start.key)
if (parent.isVoid) return
if (range.isExpanded) {
change.deleteAtRange(range, { normalize: false })
// Update range start after delete
- if (change.value.startKey !== key) {
- key = change.value.startKey
- offset = change.value.startOffset
+ if (change.value.selection.start.key !== key) {
+ key = change.value.selection.start.key
+ offset = change.value.selection.start.offset
}
}
@@ -884,7 +870,7 @@ Changes.insertTextAtRange = (change, range, text, marks, options = {}) => {
if (normalize) {
// normalize in the narrowest existing block that originally contains startKey and endKey
- const commonAncestor = document.getCommonAncestor(startKey, range.endKey)
+ const commonAncestor = document.getCommonAncestor(start.key, range.end.key)
const ancestors = document
.getAncestors(commonAncestor.key)
.push(commonAncestor)
@@ -893,7 +879,7 @@ Changes.insertTextAtRange = (change, range, text, marks, options = {}) => {
)
// it is possible that normalizeAncestor doesn't return any node
// on that case fallback to startKey to be normalized
- const normalizeKey = normalizeAncestor ? normalizeAncestor.key : startKey
+ const normalizeKey = normalizeAncestor ? normalizeAncestor.key : start.key
change.normalizeNodeByKey(normalizeKey)
}
}
@@ -915,16 +901,16 @@ Changes.removeMarkAtRange = (change, range, mark, options = {}) => {
const { value } = change
const { document } = value
const texts = document.getTextsAtRange(range)
- const { startKey, startOffset, endKey, endOffset } = range
+ const { start, end } = range
texts.forEach(node => {
const { key } = node
let index = 0
let length = node.text.length
- if (key == startKey) index = startOffset
- if (key == endKey) length = endOffset
- if (key == startKey && key == endKey) length = endOffset - startOffset
+ if (key == start.key) index = start.offset
+ if (key == end.key) length = end.offset
+ if (key == start.key && key == end.key) length = end.offset - start.offset
change.removeMarkByKey(key, index, length, mark, { normalize })
})
@@ -946,21 +932,21 @@ Changes.setBlocksAtRange = (change, range, properties, options = {}) => {
const { document } = value
const blocks = document.getBlocksAtRange(range)
- const { startKey, startOffset, endKey, endOffset, isCollapsed } = range
- const isStartVoid = document.hasVoidParent(startKey)
- const startBlock = document.getClosestBlock(startKey)
- const endBlock = document.getClosestBlock(endKey)
+ const { start, end, isCollapsed } = range
+ const isStartVoid = document.hasVoidParent(start.key)
+ const startBlock = document.getClosestBlock(start.key)
+ const endBlock = document.getClosestBlock(end.key)
// Check if we have a "hanging" selection case where the even though the
// selection extends into the start of the end node, we actually want to
// ignore that for UX reasons.
const isHanging =
isCollapsed == false &&
- startOffset == 0 &&
- endOffset == 0 &&
+ start.offset == 0 &&
+ end.offset == 0 &&
isStartVoid == false &&
- startKey == startBlock.getFirstText().key &&
- endKey == endBlock.getFirstText().key
+ start.key == startBlock.getFirstText().key &&
+ end.key == endBlock.getFirstText().key
// If it's a hanging selection, ignore the last block.
const sets = isHanging ? blocks.slice(0, -1) : blocks
@@ -1022,10 +1008,10 @@ Changes.setInlineAtRange = (...args) => {
Changes.splitBlockAtRange = (change, range, height = 1, options = {}) => {
const normalize = change.getFlag('normalize', options)
- const { startKey, startOffset, endOffset, endKey } = range
+ const { start, end } = range
const { value } = change
const { document } = value
- let node = document.assertDescendant(startKey)
+ let node = document.assertDescendant(start.key)
let parent = document.getClosestBlock(node.key)
let h = 0
@@ -1035,17 +1021,17 @@ Changes.splitBlockAtRange = (change, range, height = 1, options = {}) => {
h++
}
- change.splitDescendantsByKey(node.key, startKey, startOffset, {
+ change.splitDescendantsByKey(node.key, start.key, start.offset, {
normalize: normalize && range.isCollapsed,
})
if (range.isExpanded) {
if (range.isBackward) range = range.flip()
const nextBlock = change.value.document.getNextBlock(node.key)
- range = range.moveAnchorToStartOf(nextBlock)
+ range = range.moveAnchorToStartOfNode(nextBlock)
- if (startKey === endKey) {
- range = range.moveFocusTo(range.anchorKey, endOffset - startOffset)
+ if (start.key === end.key) {
+ range = range.moveFocusTo(range.anchor.key, end.offset - start.offset)
}
change.deleteAtRange(range, { normalize })
@@ -1072,13 +1058,13 @@ Changes.splitInlineAtRange = (
if (range.isExpanded) {
change.deleteAtRange(range, { normalize })
- range = range.collapseToStart()
+ range = range.moveToStart()
}
- const { startKey, startOffset } = range
+ const { start } = range
const { value } = change
const { document } = value
- let node = document.assertDescendant(startKey)
+ let node = document.assertDescendant(start.key)
let parent = document.getClosestInline(node.key)
let h = 0
@@ -1088,7 +1074,7 @@ Changes.splitInlineAtRange = (
h++
}
- change.splitDescendantsByKey(node.key, startKey, startOffset, { normalize })
+ change.splitDescendantsByKey(node.key, start.key, start.offset, { normalize })
}
/**
@@ -1353,11 +1339,11 @@ Changes.wrapInlineAtRange = (change, range, inline, options = {}) => {
const { value } = change
let { document } = value
const normalize = change.getFlag('normalize', options)
- const { startKey, startOffset, endKey, endOffset } = range
+ const { start, end } = range
if (range.isCollapsed) {
// Wrapping an inline void
- const inlineParent = document.getClosestInline(startKey)
+ const inlineParent = document.getClosestInline(start.key)
if (!inlineParent.isVoid) {
return
@@ -1370,19 +1356,19 @@ Changes.wrapInlineAtRange = (change, range, inline, options = {}) => {
inline = inline.set('nodes', inline.nodes.clear())
const blocks = document.getBlocksAtRange(range)
- let startBlock = document.getClosestBlock(startKey)
- let endBlock = document.getClosestBlock(endKey)
- const startInline = document.getClosestInline(startKey)
- const endInline = document.getClosestInline(endKey)
- let startChild = startBlock.getFurthestAncestor(startKey)
- let endChild = endBlock.getFurthestAncestor(endKey)
+ let startBlock = document.getClosestBlock(start.key)
+ let endBlock = document.getClosestBlock(end.key)
+ const startInline = document.getClosestInline(start.key)
+ const endInline = document.getClosestInline(end.key)
+ let startChild = startBlock.getFurthestAncestor(start.key)
+ let endChild = endBlock.getFurthestAncestor(end.key)
if (!startInline || startInline != endInline) {
- change.splitDescendantsByKey(endChild.key, endKey, endOffset, {
+ change.splitDescendantsByKey(endChild.key, end.key, end.offset, {
normalize: false,
})
- change.splitDescendantsByKey(startChild.key, startKey, startOffset, {
+ change.splitDescendantsByKey(startChild.key, start.key, start.offset, {
normalize: false,
})
}
@@ -1390,8 +1376,8 @@ Changes.wrapInlineAtRange = (change, range, inline, options = {}) => {
document = change.value.document
startBlock = document.getDescendant(startBlock.key)
endBlock = document.getDescendant(endBlock.key)
- startChild = startBlock.getFurthestAncestor(startKey)
- endChild = endBlock.getFurthestAncestor(endKey)
+ startChild = startBlock.getFurthestAncestor(start.key)
+ endChild = endBlock.getFurthestAncestor(end.key)
const startIndex = startBlock.nodes.indexOf(startChild)
const endIndex = endBlock.nodes.indexOf(endChild)
@@ -1399,28 +1385,34 @@ Changes.wrapInlineAtRange = (change, range, inline, options = {}) => {
const text = startBlock
.getTextsAtRange(range)
.get(0)
- .splitText(startOffset)[1]
- .splitText(endOffset - startOffset)[0]
+ .splitText(start.offset)[1]
+ .splitText(end.offset - start.offset)[0]
inline = inline.set('nodes', List([text]))
Changes.insertInlineAtRange(change, range, inline, { normalize: false })
const inlinekey = inline.getFirstText().key
const rng = {
- anchorKey: inlinekey,
- focusKey: inlinekey,
- anchorOffset: 0,
- focusOffset: endOffset - startOffset,
+ anchor: {
+ key: inlinekey,
+ offset: 0,
+ },
+ focus: {
+ key: inlinekey,
+ offset: end.offset - start.offset,
+ },
isFocused: true,
}
change.select(rng)
} else if (startBlock == endBlock) {
document = change.value.document
- startBlock = document.getClosestBlock(startKey)
- startChild = startBlock.getFurthestAncestor(startKey)
+ startBlock = document.getClosestBlock(start.key)
+ startChild = startBlock.getFurthestAncestor(start.key)
const startInner = document.getNextSibling(startChild.key)
const startInnerIndex = startBlock.nodes.indexOf(startInner)
const endInner =
- startKey == endKey ? startInner : startBlock.getFurthestAncestor(endKey)
+ start.key == end.key
+ ? startInner
+ : startBlock.getFurthestAncestor(end.key)
const inlines = startBlock.nodes
.skipUntil(n => n == startInner)
.takeUntil(n => n == endInner)
@@ -1499,16 +1491,16 @@ Changes.wrapTextAtRange = (
options = {}
) => {
const normalize = change.getFlag('normalize', options)
- const { startKey, endKey } = range
- const start = range.collapseToStart()
- let end = range.collapseToEnd()
+ const { start, end } = range
+ const startRange = range.moveToStart()
+ let endRange = range.moveToEnd()
- if (startKey == endKey) {
- end = end.move(prefix.length)
+ if (start.key == end.key) {
+ endRange = endRange.moveForward(prefix.length)
}
- change.insertTextAtRange(start, prefix, [], { normalize })
- change.insertTextAtRange(end, suffix, [], { normalize })
+ change.insertTextAtRange(startRange, prefix, [], { normalize })
+ change.insertTextAtRange(endRange, suffix, [], { normalize })
}
/**
diff --git a/packages/slate/src/changes/by-path.js b/packages/slate/src/changes/by-path.js
index eb72d6377..d424c4b85 100644
--- a/packages/slate/src/changes/by-path.js
+++ b/packages/slate/src/changes/by-path.js
@@ -343,10 +343,8 @@ Changes.replaceTextByPath = (
}
const range = document.createRange({
- anchorPath: path,
- focusPath: path,
- anchorOffset: offset,
- focusOffset: offset + length,
+ anchor: { path, offset },
+ focus: { path, offset: offset + length },
})
let activeMarks = document.getActiveMarksAtRange(range)
@@ -597,7 +595,7 @@ Changes.unwrapInlineByPath = (change, path, properties, options) => {
const node = document.assertNode(path)
const first = node.getFirstText()
const last = node.getLastText()
- const range = selection.moveToRangeOf(first, last)
+ const range = selection.moveToRangeOfNode(first, last)
change.unwrapInlineAtRange(range, properties, options)
}
@@ -616,7 +614,7 @@ Changes.unwrapBlockByPath = (change, path, properties, options) => {
const node = document.assertNode(path)
const first = node.getFirstText()
const last = node.getLastText()
- const range = selection.moveToRangeOf(first, last)
+ const range = selection.moveToRangeOfNode(first, last)
change.unwrapBlockAtRange(range, properties, options)
}
diff --git a/packages/slate/src/changes/on-selection.js b/packages/slate/src/changes/on-selection.js
index 2c0b4e318..e53cf3c59 100644
--- a/packages/slate/src/changes/on-selection.js
+++ b/packages/slate/src/changes/on-selection.js
@@ -1,23 +1,520 @@
import { is } from 'immutable'
import isEmpty from 'is-empty'
+import logger from 'slate-dev-logger'
import pick from 'lodash/pick'
import Range from '../models/range'
-/**
- * Changes.
- *
- * @type {Object}
- */
-
const Changes = {}
-/**
- * Set `properties` on the selection.
- *
- * @param {Change} change
- * @param {Object} properties
- */
+Changes.blur = change => {
+ change.select({ isFocused: false })
+}
+
+Changes.deselect = change => {
+ const range = Range.create()
+ change.select(range)
+}
+
+Changes.focus = change => {
+ change.select({ isFocused: true })
+}
+
+Changes.flip = change => {
+ change.call(proxy, 'flip')
+}
+
+Changes.moveAnchorBackward = (change, ...args) => {
+ change.call(pointBackward, 'anchor', ...args)
+}
+
+Changes.moveAnchorForward = (change, ...args) => {
+ change.call(pointForward, 'anchor', ...args)
+}
+
+Changes.moveAnchorTo = (change, ...args) => {
+ change.call(proxy, 'moveAnchorTo', ...args)
+}
+
+Changes.moveAnchorToEndOfBlock = change => {
+ change.call(pointEdgeObject, 'anchor', 'end', 'block')
+}
+
+Changes.moveAnchorToEndOfInline = change => {
+ change.call(pointEdgeObject, 'anchor', 'end', 'inline')
+}
+
+Changes.moveAnchorToEndOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'previous', 'block')
+}
+
+Changes.moveAnchorToEndOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'previous', 'inline')
+}
+
+Changes.moveAnchorToEndOfNextText = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'next', 'text')
+}
+
+Changes.moveAnchorToEndOfNode = (change, ...args) => {
+ change.call(proxy, 'moveAnchorToEndOfNode', ...args)
+}
+
+Changes.moveAnchorToEndOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'next', 'block')
+}
+
+Changes.moveAnchorToEndOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'next', 'inline')
+}
+
+Changes.moveAnchorToEndOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'end', 'previous', 'text')
+}
+
+Changes.moveAnchorToEndOfText = change => {
+ change.call(pointEdgeObject, 'anchor', 'end', 'text')
+}
+
+Changes.moveAnchorToStartOfBlock = change => {
+ change.call(pointEdgeObject, 'anchor', 'start', 'block')
+}
+
+Changes.moveAnchorToStartOfInline = change => {
+ change.call(pointEdgeObject, 'anchor', 'start', 'inline')
+}
+
+Changes.moveAnchorToStartOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'next', 'block')
+}
+
+Changes.moveAnchorToStartOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'next', 'inline')
+}
+
+Changes.moveAnchorToStartOfNextText = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'next', 'text')
+}
+
+Changes.moveAnchorToStartOfNode = (change, ...args) => {
+ change.call(proxy, 'moveAnchorToStartOfNode', ...args)
+}
+
+Changes.moveAnchorToStartOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'previous', 'block')
+}
+
+Changes.moveAnchorToStartOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'previous', 'inline')
+}
+
+Changes.moveAnchorToStartOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'anchor', 'start', 'previous', 'text')
+}
+
+Changes.moveAnchorToStartOfText = change => {
+ change.call(pointEdgeObject, 'anchor', 'start', 'text')
+}
+
+Changes.moveBackward = (change, ...args) => {
+ change.moveAnchorBackward(...args).moveFocusBackward(...args)
+}
+
+Changes.moveEndBackward = (change, ...args) => {
+ change.call(pointBackward, 'end', ...args)
+}
+
+Changes.moveEndForward = (change, ...args) => {
+ change.call(pointForward, 'end', ...args)
+}
+
+Changes.moveEndTo = (change, ...args) => {
+ change.call(proxy, 'moveEndTo', ...args)
+}
+
+Changes.moveEndToEndOfBlock = change => {
+ change.call(pointEdgeObject, 'end', 'end', 'block')
+}
+
+Changes.moveEndToEndOfInline = change => {
+ change.call(pointEdgeObject, 'end', 'end', 'inline')
+}
+
+Changes.moveEndToEndOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'previous', 'block')
+}
+
+Changes.moveEndToEndOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'previous', 'inline')
+}
+
+Changes.moveEndToEndOfNextText = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'next', 'text')
+}
+
+Changes.moveEndToEndOfNode = (change, ...args) => {
+ change.call(proxy, 'moveEndToEndOfNode', ...args)
+}
+
+Changes.moveEndToEndOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'next', 'block')
+}
+
+Changes.moveEndToEndOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'next', 'inline')
+}
+
+Changes.moveEndToEndOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'end', 'end', 'previous', 'text')
+}
+
+Changes.moveEndToEndOfText = change => {
+ change.call(pointEdgeObject, 'end', 'end', 'text')
+}
+
+Changes.moveEndToStartOfBlock = change => {
+ change.call(pointEdgeObject, 'end', 'start', 'block')
+}
+
+Changes.moveEndToStartOfInline = change => {
+ change.call(pointEdgeObject, 'end', 'start', 'inline')
+}
+
+Changes.moveEndToStartOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'next', 'block')
+}
+
+Changes.moveEndToStartOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'next', 'inline')
+}
+
+Changes.moveEndToStartOfNextText = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'next', 'text')
+}
+
+Changes.moveEndToStartOfNode = (change, ...args) => {
+ change.call(proxy, 'moveEndToStartOfNode', ...args)
+}
+
+Changes.moveEndToStartOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'previous', 'block')
+}
+
+Changes.moveEndToStartOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'previous', 'inline')
+}
+
+Changes.moveEndToStartOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'end', 'start', 'previous', 'text')
+}
+
+Changes.moveEndToStartOfText = change => {
+ change.call(pointEdgeObject, 'end', 'start', 'text')
+}
+
+Changes.moveFocusBackward = (change, ...args) => {
+ change.call(pointBackward, 'focus', ...args)
+}
+
+Changes.moveFocusForward = (change, ...args) => {
+ change.call(pointForward, 'focus', ...args)
+}
+
+Changes.moveFocusTo = (change, ...args) => {
+ change.call(proxy, 'moveFocusTo', ...args)
+}
+
+Changes.moveFocusToEndOfBlock = change => {
+ change.call(pointEdgeObject, 'focus', 'end', 'block')
+}
+
+Changes.moveFocusToEndOfInline = change => {
+ change.call(pointEdgeObject, 'focus', 'end', 'inline')
+}
+
+Changes.moveFocusToEndOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'previous', 'block')
+}
+
+Changes.moveFocusToEndOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'previous', 'inline')
+}
+
+Changes.moveFocusToEndOfNextText = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'next', 'text')
+}
+
+Changes.moveFocusToEndOfNode = (change, ...args) => {
+ change.call(proxy, 'moveFocusToEndOfNode', ...args)
+}
+
+Changes.moveFocusToEndOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'next', 'block')
+}
+
+Changes.moveFocusToEndOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'next', 'inline')
+}
+
+Changes.moveFocusToEndOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'focus', 'end', 'previous', 'text')
+}
+
+Changes.moveFocusToEndOfText = change => {
+ change.call(pointEdgeObject, 'focus', 'end', 'text')
+}
+
+Changes.moveFocusToStartOfBlock = change => {
+ change.call(pointEdgeObject, 'focus', 'start', 'block')
+}
+
+Changes.moveFocusToStartOfInline = change => {
+ change.call(pointEdgeObject, 'focus', 'start', 'inline')
+}
+
+Changes.moveFocusToStartOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'next', 'block')
+}
+
+Changes.moveFocusToStartOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'next', 'inline')
+}
+
+Changes.moveFocusToStartOfNextText = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'next', 'text')
+}
+
+Changes.moveFocusToStartOfNode = (change, ...args) => {
+ change.call(proxy, 'moveFocusToStartOfNode', ...args)
+}
+
+Changes.moveFocusToStartOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'previous', 'block')
+}
+
+Changes.moveFocusToStartOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'previous', 'inline')
+}
+
+Changes.moveFocusToStartOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'focus', 'start', 'previous', 'text')
+}
+
+Changes.moveFocusToStartOfText = change => {
+ change.call(pointEdgeObject, 'focus', 'start', 'text')
+}
+
+Changes.moveForward = (change, ...args) => {
+ change.moveAnchorForward(...args).moveFocusForward(...args)
+}
+
+Changes.moveStartBackward = (change, ...args) => {
+ change.call(pointBackward, 'start', ...args)
+}
+
+Changes.moveStartForward = (change, ...args) => {
+ change.call(pointForward, 'start', ...args)
+}
+
+Changes.moveStartTo = (change, ...args) => {
+ change.call(proxy, 'moveStartTo', ...args)
+}
+
+Changes.moveStartToEndOfBlock = change => {
+ change.call(pointEdgeObject, 'start', 'end', 'block')
+}
+
+Changes.moveStartToEndOfInline = change => {
+ change.call(pointEdgeObject, 'start', 'end', 'inline')
+}
+
+Changes.moveStartToEndOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'previous', 'block')
+}
+
+Changes.moveStartToEndOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'previous', 'inline')
+}
+
+Changes.moveStartToEndOfNextText = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'next', 'text')
+}
+
+Changes.moveStartToEndOfNode = (change, ...args) => {
+ change.call(proxy, 'moveStartToEndOfNode', ...args)
+}
+
+Changes.moveStartToEndOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'next', 'block')
+}
+
+Changes.moveStartToEndOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'next', 'inline')
+}
+
+Changes.moveStartToEndOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'start', 'end', 'previous', 'text')
+}
+
+Changes.moveStartToEndOfText = change => {
+ change.call(pointEdgeObject, 'start', 'end', 'text')
+}
+
+Changes.moveStartToStartOfBlock = change => {
+ change.call(pointEdgeObject, 'start', 'start', 'block')
+}
+
+Changes.moveStartToStartOfInline = change => {
+ change.call(pointEdgeObject, 'start', 'start', 'inline')
+}
+
+Changes.moveStartToStartOfNextBlock = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'next', 'block')
+}
+
+Changes.moveStartToStartOfNextInline = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'next', 'inline')
+}
+
+Changes.moveStartToStartOfNextText = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'next', 'text')
+}
+
+Changes.moveStartToStartOfNode = (change, ...args) => {
+ change.call(proxy, 'moveStartToStartOfNode', ...args)
+}
+
+Changes.moveStartToStartOfPreviousBlock = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'previous', 'block')
+}
+
+Changes.moveStartToStartOfPreviousInline = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'previous', 'inline')
+}
+
+Changes.moveStartToStartOfPreviousText = change => {
+ change.call(pointEdgeSideObject, 'start', 'start', 'previous', 'text')
+}
+
+Changes.moveStartToStartOfText = change => {
+ change.call(pointEdgeObject, 'start', 'start', 'text')
+}
+
+Changes.moveTo = (change, ...args) => {
+ change.call(proxy, 'moveTo', ...args)
+}
+
+Changes.moveToAnchor = change => {
+ change.call(proxy, 'moveToAnchor')
+}
+
+Changes.moveToEnd = change => {
+ change.call(proxy, 'moveToEnd')
+}
+
+Changes.moveToEndOfBlock = change => {
+ change.moveEndToEndOfBlock().moveToEnd()
+}
+
+Changes.moveToEndOfDocument = change => {
+ change.moveEndToEndOfNode(change.value.document).moveToEnd()
+}
+
+Changes.moveToEndOfInline = change => {
+ change.moveEndToEndOfInline().moveToEnd()
+}
+
+Changes.moveToEndOfNextBlock = change => {
+ change.moveEndToEndOfNextBlock().moveToEnd()
+}
+
+Changes.moveToEndOfNextInline = change => {
+ change.moveEndToEndOfNextInline().moveToEnd()
+}
+
+Changes.moveToEndOfNextText = change => {
+ change.moveEndToEndOfNextText().moveToEnd()
+}
+
+Changes.moveToEndOfNode = (change, ...args) => {
+ change.call(proxy, 'moveToEndOfNode', ...args)
+}
+
+Changes.moveToEndOfPreviousBlock = change => {
+ change.moveStartToEndOfPreviousBlock().moveToStart()
+}
+
+Changes.moveToEndOfPreviousInline = change => {
+ change.moveStartToEndOfPreviousInline().moveToStart()
+}
+
+Changes.moveToEndOfPreviousText = change => {
+ change.moveStartToEndOfPreviousText().moveToStart()
+}
+
+Changes.moveToEndOfText = change => {
+ change.moveEndToEndOfText().moveToEnd()
+}
+
+Changes.moveToFocus = change => {
+ change.call(proxy, 'moveToFocus')
+}
+
+Changes.moveToRangeOfDocument = change => {
+ change.moveToRangeOfNode(change.value.document)
+}
+
+Changes.moveToRangeOfNode = (change, ...args) => {
+ change.call(proxy, 'moveToRangeOfNode', ...args)
+}
+
+Changes.moveToStart = change => {
+ change.call(proxy, 'moveToStart')
+}
+
+Changes.moveToStartOfBlock = change => {
+ change.moveStartToStartOfBlock().moveToStart()
+}
+
+Changes.moveToStartOfDocument = change => {
+ change.moveStartToStartOfNode(change.value.document).moveToStart()
+}
+
+Changes.moveToStartOfInline = change => {
+ change.moveStartToStartOfInline().moveToStart()
+}
+
+Changes.moveToStartOfNextBlock = change => {
+ change.moveEndToStartOfNextBlock().moveToEnd()
+}
+
+Changes.moveToStartOfNextInline = change => {
+ change.moveEndToStartOfNextInline().moveToEnd()
+}
+
+Changes.moveToStartOfNextText = change => {
+ change.moveEndToStartOfNextText().moveToEnd()
+}
+
+Changes.moveToStartOfNode = (change, ...args) => {
+ change.call(proxy, 'moveToStartOfNode', ...args)
+}
+
+Changes.moveToStartOfPreviousBlock = change => {
+ change.moveStartToStartOfPreviousBlock().moveToStart()
+}
+
+Changes.moveToStartOfPreviousInline = change => {
+ change.moveStartToStartOfPreviousInline().moveToStart()
+}
+
+Changes.moveToStartOfPreviousText = change => {
+ change.moveStartToStartOfPreviousText().moveToStart()
+}
+
+Changes.moveToStartOfText = change => {
+ change.moveStartToStartOfText().moveToStart()
+}
Changes.select = (change, properties, options = {}) => {
properties = Range.createProperties(properties)
@@ -25,7 +522,8 @@ Changes.select = (change, properties, options = {}) => {
const { value } = change
const { document, selection } = value
const props = {}
- const next = document.createRange(selection.merge(properties))
+ let next = selection.setProperties(properties)
+ next = document.resolveRange(next)
// Re-compute the properties, to ensure that we get their normalized values.
properties = pick(next, Object.keys(properties))
@@ -41,14 +539,7 @@ Changes.select = (change, properties, options = {}) => {
// If the selection moves, clear any marks, unless the new selection
// properties change the marks in some way.
- if (
- selection.marks &&
- !props.marks &&
- (props.hasOwnProperty('anchorKey') ||
- props.hasOwnProperty('anchorOffset') ||
- props.hasOwnProperty('focusKey') ||
- props.hasOwnProperty('focusOffset'))
- ) {
+ if (selection.marks && !props.marks && (props.anchor || props.focus)) {
props.marks = null
}
@@ -68,329 +559,228 @@ Changes.select = (change, properties, options = {}) => {
)
}
-/**
- * Select the whole document.
- *
- * @param {Change} change
- */
-
-Changes.selectAll = change => {
- const { value } = change
- const { document, selection } = value
- const next = selection.moveToRangeOf(document)
- change.select(next)
+Changes.setAnchor = (change, ...args) => {
+ change.call(proxy, 'setAnchor', ...args)
}
-/**
- * Snapshot the current selection.
- *
- * @param {Change} change
- */
+Changes.setEnd = (change, ...args) => {
+ change.call(proxy, 'setEnd', ...args)
+}
+
+Changes.setFocus = (change, ...args) => {
+ change.call(proxy, 'setFocus', ...args)
+}
+
+Changes.setStart = (change, ...args) => {
+ change.call(proxy, 'setStart', ...args)
+}
Changes.snapshotSelection = change => {
- const { value } = change
- const { selection } = value
- change.select(selection, { snapshot: true })
+ change.select(change.value.selection, { snapshot: true })
}
/**
- * Move the anchor point backward, accounting for being at the start of a block.
- *
- * @param {Change} change
+ * Helpers.
*/
-Changes.moveAnchorCharBackward = change => {
+function proxy(change, method, ...args) {
+ const range = change.value.selection[method](...args)
+ change.select(range)
+}
+
+function pointEdgeObject(change, point, edge, object) {
+ const Point = point.slice(0, 1).toUpperCase() + point.slice(1)
+ const Edge = edge.slice(0, 1).toUpperCase() + edge.slice(1)
+ const Object = object.slice(0, 1).toUpperCase() + object.slice(1)
+ const method = `move${Point}To${Edge}OfNode`
+ const getNode = object == 'text' ? 'getNode' : `getClosest${Object}`
const { value } = change
- const { document, selection, anchorText, anchorBlock } = value
- const { anchorOffset } = selection
- const previousText = document.getPreviousText(anchorText.key)
- const isInVoid = document.hasVoidParent(anchorText.key)
- const isPreviousInVoid =
- previousText && document.hasVoidParent(previousText.key)
+ const { document, selection } = value
+ const p = selection[point]
+ const node = document[getNode](p.key)
+ if (!node) return
+ change[method](node)
+}
- if (!isInVoid && anchorOffset > 0) {
- change.moveAnchor(-1)
+function pointEdgeSideObject(change, point, edge, side, object) {
+ const Point = point.slice(0, 1).toUpperCase() + point.slice(1)
+ const Edge = edge.slice(0, 1).toUpperCase() + edge.slice(1)
+ const Side = side.slice(0, 1).toUpperCase() + side.slice(1)
+ const Object = object.slice(0, 1).toUpperCase() + object.slice(1)
+ const method = `move${Point}To${Edge}OfNode`
+ const getNode = object == 'text' ? 'getNode' : `getClosest${Object}`
+ const getDirectionNode = `get${Side}${Object}`
+ const { value } = change
+ const { document, selection } = value
+ const p = selection[point]
+ const node = document[getNode](p.key)
+ if (!node) return
+ const target = document[getDirectionNode](node.key)
+ if (!target) return
+ change[method](node)
+}
+
+function pointBackward(change, point, n = 1) {
+ if (n === 0) return
+ if (n < 0) return pointForward(change, point, -n)
+
+ const Point = point.slice(0, 1).toUpperCase() + point.slice(1)
+ const { value } = change
+ const { document, selection } = value
+ const p = selection[point]
+ const isInVoid = document.hasVoidParent(p.path)
+
+ if (!isInVoid && p.offset - n >= 0) {
+ const range = selection[`move${Point}Backward`](n)
+ change.select(range)
return
}
- if (!previousText) {
+ const previous = document.getPreviousText(p.path)
+ if (!previous) return
+
+ const block = document.getClosestBlock(p.path)
+ const isInBlock = block.hasNode(previous.key)
+ const isPreviousInVoid = previous && document.hasVoidParent(previous.key)
+ change.moveToEndOfNode(previous)
+
+ if (!isInVoid && !isPreviousInVoid && isInBlock) {
+ const range = change.value.selection[`move${Point}Backward`](n)
+ change.select(range)
+ }
+}
+
+function pointForward(change, point, n = 1) {
+ if (n === 0) return
+ if (n < 0) return pointBackward(change, point, -n)
+
+ const Point = point.slice(0, 1).toUpperCase() + point.slice(1)
+ const { value } = change
+ const { document, selection } = value
+ const p = selection[point]
+ const text = document.getNode(p.path)
+ const isInVoid = document.hasVoidParent(p.path)
+
+ if (!isInVoid && p.offset + n <= text.text.length) {
+ const range = selection[`move${Point}Forward`](n)
+ change.select(range)
return
}
- change.moveAnchorToEndOf(previousText)
+ const block = document.getClosestBlock(p.path)
+ const isInBlock = block.hasNode(next.key)
+ const next = document.getNextText(p.path)
+ const isNextInVoid = next && document.hasVoidParent(next.key)
+ if (!next) return
- if (!isInVoid && !isPreviousInVoid && anchorBlock.hasNode(previousText.key)) {
- change.moveAnchor(-1)
+ change.moveAnchorToStartOf(next)
+
+ if (!isInVoid && !isNextInVoid && isInBlock) {
+ const range = change.value.selection[`move${Point}Forward`](n)
+ change.select(range)
}
}
/**
- * Move the anchor point forward, accounting for being at the end of a block.
- *
- * @param {Change} change
+ * Deprecated.
*/
-Changes.moveAnchorCharForward = change => {
- const { value } = change
- const { document, selection, anchorText, anchorBlock } = value
- const { anchorOffset } = selection
- const nextText = document.getNextText(anchorText.key)
- const isInVoid = document.hasVoidParent(anchorText.key)
- const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
+Changes.moveOffsetsTo = (change, start, end = start) => {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Change.moveOffsetsTo` method is deprecated, please use `Change.moveAnchorTo` and `Change.moveFocusTo` instead.'
+ )
- if (!isInVoid && anchorOffset < anchorText.text.length) {
- change.moveAnchor(1)
- return
- }
-
- if (!nextText) {
- return
- }
-
- change.moveAnchorToStartOf(nextText)
-
- if (!isInVoid && !isNextInVoid && anchorBlock.hasNode(nextText.key)) {
- change.moveAnchor(1)
- }
+ change.moveAnchorTo(start).moveFocusTo(end)
}
-/**
- * Move the focus point backward, accounting for being at the start of a block.
- *
- * @param {Change} change
- */
-
-Changes.moveFocusCharBackward = change => {
- const { value } = change
- const { document, selection, focusText, focusBlock } = value
- const { focusOffset } = selection
- const previousText = document.getPreviousText(focusText.key)
- const isInVoid = document.hasVoidParent(focusText.key)
- const isPreviousInVoid =
- previousText && document.hasVoidParent(previousText.key)
-
- if (!isInVoid && focusOffset > 0) {
- change.moveFocus(-1)
- return
- }
-
- if (!previousText) {
- return
- }
-
- change.moveFocusToEndOf(previousText)
-
- if (!isInVoid && !isPreviousInVoid && focusBlock.hasNode(previousText.key)) {
- change.moveFocus(-1)
- }
-}
-
-/**
- * Move the focus point forward, accounting for being at the end of a block.
- *
- * @param {Change} change
- */
-
-Changes.moveFocusCharForward = change => {
- const { value } = change
- const { document, selection, focusText, focusBlock } = value
- const { focusOffset } = selection
- const nextText = document.getNextText(focusText.key)
- const isInVoid = document.hasVoidParent(focusText.key)
- const isNextInVoid = nextText && document.hasVoidParent(nextText.key)
-
- if (!isInVoid && focusOffset < focusText.text.length) {
- change.moveFocus(1)
- return
- }
-
- if (!nextText) {
- return
- }
-
- change.moveFocusToStartOf(nextText)
-
- if (!isInVoid && !isNextInVoid && focusBlock.hasNode(nextText.key)) {
- change.moveFocus(1)
- }
-}
-
-/**
- * Mix in move methods.
- */
-
-const MOVE_DIRECTIONS = ['Forward', 'Backward']
-
-MOVE_DIRECTIONS.forEach(direction => {
- const anchor = `moveAnchorChar${direction}`
- const focus = `moveFocusChar${direction}`
-
- Changes[`moveChar${direction}`] = change => {
- change[anchor]()[focus]()
- }
-
- Changes[`moveStartChar${direction}`] = change => {
- if (change.value.isBackward) {
- change[focus]()
- } else {
- change[anchor]()
- }
- }
-
- Changes[`moveEndChar${direction}`] = change => {
- if (change.value.isBackward) {
- change[anchor]()
- } else {
- change[focus]()
- }
- }
-
- Changes[`extendChar${direction}`] = change => {
- change[`moveFocusChar${direction}`]()
- }
-
- Changes[`collapseChar${direction}`] = change => {
- const collapse =
- direction == 'Forward' ? 'collapseToEnd' : 'collapseToStart'
- change[collapse]()[`moveChar${direction}`]()
- }
-})
-
-/**
- * Mix in alias methods.
- */
-
-const ALIAS_METHODS = [
- ['collapseLineBackward', 'collapseToStartOfBlock'],
- ['collapseLineForward', 'collapseToEndOfBlock'],
- ['extendLineBackward', 'extendToStartOfBlock'],
- ['extendLineForward', 'extendToEndOfBlock'],
+const DEPRECATEDS = [
+ ['collapseCharBackward', 'moveBackward'],
+ ['collapseCharForward', 'moveForward'],
+ ['collapseLineBackward', 'moveLineBackward'],
+ ['collapseLineForward', 'moveLineForward'],
+ ['collapseTo', 'moveTo'],
+ ['collapseToAnchor', 'moveToAnchor'],
+ ['collapseToEnd', 'moveToEnd'],
+ ['collapseToEndOf', 'moveToEndOfNode'],
+ ['collapseToEndOfBlock', 'moveToEndOfBlock'],
+ ['collapseToEndOfNextBlock', 'moveToEndOfNextBlock'],
+ ['collapseToEndOfNextInline', 'moveToEndOfNextInline'],
+ ['collapseToEndOfNextText', 'moveToEndOfNextText'],
+ ['collapseToEndOfPreviousBlock', 'moveToEndOfPreviousBlock'],
+ ['collapseToEndOfPreviousInline', 'moveToEndOfPreviousInline'],
+ ['collapseToEndOfPreviousText', 'moveToEndOfPreviousText'],
+ ['collapseToFocus', 'moveToFocus'],
+ ['collapseToStart', 'moveToStart'],
+ ['collapseToStartOf', 'moveToStartOfNode'],
+ ['collapseToStartOfBlock', 'moveToStartOfBlock'],
+ ['collapseToStartOfNextBlock', 'moveToStartOfNextBlock'],
+ ['collapseToStartOfNextInline', 'moveToStartOfNextInline'],
+ ['collapseToStartOfNextText', 'moveToStartOfNextText'],
+ ['collapseToStartOfPreviousBlock', 'moveToStartOfPreviousBlock'],
+ ['collapseToStartOfPreviousInline', 'moveToStartOfPreviousInline'],
+ ['collapseToStartOfPreviousText', 'moveToStartOfPreviousText'],
+ ['extend', 'moveFocusForward'],
+ ['extendCharBackward', 'moveFocusBackward'],
+ ['extendCharForward', 'moveFocusForward'],
+ ['extendLineBackward', 'moveFocusToStartOfBlock'],
+ ['extendLineForward', 'moveFocusToEndOfBlock'],
+ ['extendTo', 'moveFocusTo'],
+ ['extendToEndOf', 'moveFocusToEndOfNode'],
+ ['extendToEndOfBlock', 'moveFocusToEndOfBlock'],
+ ['extendToEndOfBlock', 'moveFocusToEndOfBlock'],
+ ['extendToEndOfNextBlock', 'moveFocusToEndOfNextBlock'],
+ ['extendToEndOfNextInline', 'moveFocusToEndOfNextInline'],
+ ['extendToEndOfNextText', 'moveFocusToEndOfNextText'],
+ ['extendToEndOfPreviousBlock', 'moveFocusToEndOfPreviousBlock'],
+ ['extendToEndOfPreviousInline', 'moveFocusToEndOfPreviousInline'],
+ ['extendToEndOfPreviousText', 'moveFocusToEndOfPreviousText'],
+ ['extendToStartOf', 'moveFocusToStartOfNode'],
+ ['extendToStartOfBlock', 'moveFocusToStartOfBlock'],
+ ['extendToStartOfNextBlock', 'moveFocusToStartOfNextBlock'],
+ ['extendToStartOfNextInline', 'moveFocusToStartOfNextInline'],
+ ['extendToStartOfNextText', 'moveFocusToStartOfNextText'],
+ ['extendToStartOfPreviousBlock', 'moveFocusToStartOfPreviousBlock'],
+ ['extendToStartOfPreviousInline', 'moveFocusToStartOfPreviousInline'],
+ ['extendToStartOfPreviousText', 'moveFocusToStartOfPreviousText'],
+ ['move', 'moveForward'],
+ ['moveAnchor', 'moveAnchorForward'],
+ ['moveAnchorCharBackward', 'moveAnchorBackward'],
+ ['moveAnchorCharForward', 'moveAnchorForward'],
+ ['moveAnchorOffsetTo', 'moveAnchorTo'],
+ ['moveAnchorToEndOf', 'moveAnchorToEndOfNode'],
+ ['moveAnchorToStartOf', 'moveAnchorToStartOfNode'],
+ ['moveCharBackward', 'moveBackward'],
+ ['moveCharForward', 'moveForward'],
+ ['moveEnd', 'moveEndForward'],
+ ['moveEndCharBackward', 'moveEndBackward'],
+ ['moveEndCharForward', 'moveEndForward'],
+ ['moveEndOffsetTo', 'moveEndTo'],
+ ['moveFocus', 'moveFocusForward'],
+ ['moveFocusCharBackward', 'moveFocusBackward'],
+ ['moveFocusCharForward', 'moveFocusForward'],
+ ['moveFocusOffsetTo', 'moveFocusTo'],
+ ['moveFocusToEndOf', 'moveFocusToEndOfNode'],
+ ['moveFocusToStartOf', 'moveFocusToStartOfNode'],
+ ['moveStart', 'moveStartForward'],
+ ['moveStartCharBackward', 'moveStartBackward'],
+ ['moveStartCharForward', 'moveStartForward'],
+ ['moveStartOffsetTo', 'moveStartTo'],
+ ['moveToEndOf', 'moveToEndOfNode'],
+ ['moveToRangeOf', 'moveToRangeOfNode'],
+ ['moveToStartOf', 'moveToStartOfNode'],
+ ['selectAll', 'moveToRangeOfDocument'],
]
-ALIAS_METHODS.forEach(([alias, method]) => {
- Changes[alias] = function(change, ...args) {
- change[method](change, ...args)
+DEPRECATEDS.forEach(([deprecated, method]) => {
+ Changes[deprecated] = function(change, ...args) {
+ logger.deprecate(
+ '0.37.0',
+ `The \`Change.${deprecated}\` method is deprecated, please use \`Change.${method}\` instead.`
+ )
+
+ change[method](...args)
}
})
-/**
- * Mix in selection changes that are just a proxy for the selection method.
- */
-
-const PROXY_TRANSFORMS = [
- 'blur',
- 'collapseTo',
- 'collapseToAnchor',
- 'collapseToEnd',
- 'collapseToEndOf',
- 'collapseToFocus',
- 'collapseToStart',
- 'collapseToStartOf',
- 'extend',
- 'extendTo',
- 'extendToEndOf',
- 'extendToStartOf',
- 'flip',
- 'focus',
- 'move',
- 'moveAnchor',
- 'moveAnchorOffsetTo',
- 'moveAnchorTo',
- 'moveAnchorToEndOf',
- 'moveAnchorToStartOf',
- 'moveEnd',
- 'moveEndOffsetTo',
- 'moveEndTo',
- 'moveFocus',
- 'moveFocusOffsetTo',
- 'moveFocusTo',
- 'moveFocusToEndOf',
- 'moveFocusToStartOf',
- 'moveOffsetsTo',
- 'moveStart',
- 'moveStartOffsetTo',
- 'moveStartTo',
- 'moveTo',
- 'moveToEnd',
- 'moveToEndOf',
- 'moveToRangeOf',
- 'moveToStart',
- 'moveToStartOf',
- 'deselect',
-]
-
-PROXY_TRANSFORMS.forEach(method => {
- Changes[method] = (change, ...args) => {
- const normalize = method != 'deselect'
- const { value } = change
- const { document, selection } = value
- let next = selection[method](...args)
- if (normalize) next = document.createRange(next)
- change.select(next)
- }
-})
-
-/**
- * Mix in node-related changes.
- */
-
-const PREFIXES = [
- 'moveTo',
- 'moveAnchorTo',
- 'moveFocusTo',
- 'moveStartTo',
- 'moveEndTo',
- 'collapseTo',
- 'extendTo',
-]
-
-const DIRECTIONS = ['Next', 'Previous']
-
-const OBJECTS = ['Block', 'Inline', 'Text']
-
-PREFIXES.forEach(prefix => {
- const edges = ['Start', 'End']
-
- if (prefix == 'moveTo') {
- edges.push('Range')
- }
-
- edges.forEach(edge => {
- const method = `${prefix}${edge}Of`
-
- OBJECTS.forEach(object => {
- const getNode = object == 'Text' ? 'getNode' : `getClosest${object}`
-
- Changes[`${method}${object}`] = change => {
- const { value } = change
- const { document, selection } = value
- const node = document[getNode](selection.startKey)
- if (!node) return
- change[method](node)
- }
-
- DIRECTIONS.forEach(direction => {
- const getDirectionNode = `get${direction}${object}`
- const directionKey = direction == 'Next' ? 'startKey' : 'endKey'
-
- Changes[`${method}${direction}${object}`] = change => {
- const { value } = change
- const { document, selection } = value
- const node = document[getNode](selection[directionKey])
- if (!node) return
- const target = document[getDirectionNode](node.key)
- if (!target) return
- change[method](target)
- }
- })
- })
- })
-})
-
-/**
- * Export.
- *
- * @type {Object}
- */
-
export default Changes
diff --git a/packages/slate/src/constants/model-types.js b/packages/slate/src/constants/model-types.js
index 4f7066992..e031b8f70 100644
--- a/packages/slate/src/constants/model-types.js
+++ b/packages/slate/src/constants/model-types.js
@@ -14,6 +14,7 @@ const MODEL_TYPES = {
LEAF: '@@__SLATE_LEAF__@@',
MARK: '@@__SLATE_MARK__@@',
OPERATION: '@@__SLATE_OPERATION__@@',
+ POINT: '@@__SLATE_POINT__@@',
RANGE: '@@__SLATE_RANGE__@@',
SCHEMA: '@@__SLATE_SCHEMA__@@',
STACK: '@@__SLATE_STACK__@@',
diff --git a/packages/slate/src/index.js b/packages/slate/src/index.js
index a89fde144..d858c18c6 100644
--- a/packages/slate/src/index.js
+++ b/packages/slate/src/index.js
@@ -13,6 +13,7 @@ import Node from './models/node'
import Operation from './models/operation'
import Operations from './operations'
import PathUtils from './utils/path-utils'
+import Point from './models/point'
import Range from './models/range'
import Schema from './models/schema'
import Stack from './models/stack'
@@ -44,6 +45,7 @@ export {
Operation,
Operations,
PathUtils,
+ Point,
Range,
resetKeyGenerator,
resetMemoization,
@@ -71,6 +73,7 @@ export default {
Operation,
Operations,
PathUtils,
+ Point,
Range,
resetKeyGenerator,
resetMemoization,
diff --git a/packages/slate/src/models/history.js b/packages/slate/src/models/history.js
index 1eb9e12bb..39444bd0e 100644
--- a/packages/slate/src/models/history.js
+++ b/packages/slate/src/models/history.js
@@ -140,10 +140,6 @@ class History extends Record(DEFAULTS) {
const prevBatch = undos.peek()
const prevOperation = prevBatch && prevBatch.last()
- if (skip == null) {
- skip = shouldSkip(operation, prevOperation)
- }
-
if (skip) {
return history
}
@@ -232,22 +228,6 @@ function shouldMerge(o, p) {
return merge
}
-/**
- * Check whether to skip a new operation `o`, given previous operation `p`.
- *
- * @param {Object} o
- * @param {Object} p
- * @return {Boolean}
- */
-
-function shouldSkip(o, p) {
- if (!p) return false
-
- const skip = o.type == 'set_selection' && p.type == 'set_selection'
-
- return skip
-}
-
/**
* Export.
*
diff --git a/packages/slate/src/models/node.js b/packages/slate/src/models/node.js
index ae1caea1b..b72ec63a8 100644
--- a/packages/slate/src/models/node.js
+++ b/packages/slate/src/models/node.js
@@ -10,6 +10,7 @@ import Inline from './inline'
import KeyUtils from '../utils/key-utils'
import memoize from '../utils/memoize'
import PathUtils from '../utils/path-utils'
+import Point from './point'
import Range from './range'
import Text from './text'
import { isType } from '../constants/model-types'
@@ -206,7 +207,24 @@ class Node {
}
/**
+<<<<<<< HEAD
+ * Create a point with `properties` relative to the node.
+ *
+ * @param {Object|Point} properties
+ * @return {Range}
+ */
+
+ createPoint(properties) {
+ properties = Point.createProperties(properties)
+ const point = this.resolvePoint(properties)
+ return point
+ }
+
+ /**
+ * Create a range with `properties` relative to the node.
+=======
* Create a new range with `properties` relative to the node.
+>>>>>>> master
*
* @param {Object|Range} properties
* @return {Range}
@@ -292,11 +310,15 @@ class Node {
if (range.isUnset) return Set()
if (range.isCollapsed) {
- const { startKey, startOffset } = range
- return this.getMarksAtPosition(startKey, startOffset).toSet()
+ const { start } = range
+ return this.getMarksAtPosition(start.key, start.offset).toSet()
}
- let { startKey, endKey, startOffset, endOffset } = range
+ const { start, end } = range
+ let startKey = start.key
+ let startOffset = start.offset
+ let endKey = end.key
+ let endOffset = end.offset
let startText = this.getDescendant(startKey)
if (startKey !== endKey) {
@@ -413,18 +435,18 @@ class Node {
range = this.resolveRange(range)
if (range.isUnset) return []
- const { startKey, endKey } = range
- const startBlock = this.getClosestBlock(startKey)
+ const { start, end } = range
+ const startBlock = this.getClosestBlock(start.key)
// PERF: the most common case is when the range is in a single block node,
// where we can avoid a lot of iterating of the tree.
- if (startKey === endKey) return [startBlock]
+ if (start.key === end.key) return [startBlock]
- const endBlock = this.getClosestBlock(endKey)
+ const endBlock = this.getClosestBlock(end.key)
const blocks = this.getBlocksAsArray()
- const start = blocks.indexOf(startBlock)
- const end = blocks.indexOf(endBlock)
- return blocks.slice(start, end + 1)
+ const startIndex = blocks.indexOf(startBlock)
+ const endIndex = blocks.indexOf(endBlock)
+ return blocks.slice(startIndex, endIndex + 1)
}
/**
@@ -480,20 +502,21 @@ class Node {
getCharactersAtRange(range) {
range = this.resolveRange(range)
if (range.isUnset) return List()
- const { startKey, endKey, startOffset, endOffset } = range
- if (startKey === endKey) {
- const endText = this.getDescendant(endKey)
- return endText.characters.slice(startOffset, endOffset)
+ const { start, end } = range
+
+ if (start.key === end.key) {
+ const endText = this.getDescendant(end.key)
+ return endText.characters.slice(start.offset, end.offset)
}
return this.getTextsAtRange(range).flatMap(t => {
- if (t.key === startKey) {
- return t.characters.slice(startOffset)
+ if (t.key === start.key) {
+ return t.characters.slice(start.offset)
}
- if (t.key === endKey) {
- return t.characters.slice(0, endOffset)
+ if (t.key === end.key) {
+ return t.characters.slice(0, end.offset)
}
return t.characters
})
@@ -691,10 +714,10 @@ class Node {
return Document.create()
}
- const { startPath, startOffset, endPath, endOffset } = range
+ const { start, end } = range
let node = this
- let targetPath = endPath
- let targetPosition = endOffset
+ let targetPath = end.path
+ let targetPosition = end.offset
let mode = 'end'
while (targetPath.size) {
@@ -704,14 +727,14 @@ class Node {
targetPath = PathUtils.lift(targetPath)
if (!targetPath.size && mode === 'end') {
- targetPath = startPath
- targetPosition = startOffset
+ targetPath = start.path
+ targetPosition = start.offset
mode = 'start'
}
}
- const startIndex = startPath.first() + 1
- const endIndex = endPath.first() + 2
+ const startIndex = start.path.first() + 1
+ const endIndex = end.path.first() + 2
const nodes = node.nodes.slice(startIndex, endIndex)
const fragment = Document.create({ nodes })
return fragment
@@ -906,16 +929,19 @@ class Node {
getInsertMarksAtRange(range) {
range = this.resolveRange(range)
- if (range.isUnset) return Set()
+ const { start } = range
+
+ if (range.isUnset) {
+ return Set()
+ }
if (range.isCollapsed) {
// PERF: range is not cachable, use key and offset as proxies for cache
- return this.getMarksAtPosition(range.startKey, range.startOffset)
+ return this.getMarksAtPosition(start.key, start.offset)
}
- const { startKey, startOffset } = range
- const text = this.getDescendant(startKey)
- const marks = text.getMarksAtIndex(startOffset + 1)
+ const text = this.getDescendant(start.key)
+ const marks = text.getMarksAtIndex(start.offset + 1)
return marks
}
@@ -1201,8 +1227,8 @@ class Node {
throw new Error('The range must be collapsed to calculcate its offset.')
}
- const { startKey, startOffset } = range
- const offset = this.getOffset(startKey) + startOffset
+ const { start } = range
+ const offset = this.getOffset(start.key) + start.offset
return offset
}
@@ -1227,19 +1253,22 @@ class Node {
getOrderedMarksAtRange(range) {
range = this.resolveRange(range)
- if (range.isUnset) return OrderedSet()
+ const { start, end } = range
+
+ if (range.isUnset) {
+ return OrderedSet()
+ }
if (range.isCollapsed) {
// PERF: range is not cachable, use key and offset as proxies for cache
- return this.getMarksAtPosition(range.startKey, range.startOffset)
+ return this.getMarksAtPosition(start.key, start.offset)
}
- const { startKey, startOffset, endKey, endOffset } = range
const marks = this.getOrderedMarksBetweenPositions(
- startKey,
- startOffset,
- endKey,
- endOffset
+ start.key,
+ start.offset,
+ end.key,
+ end.offset
)
return marks
@@ -1421,7 +1450,7 @@ class Node {
*/
getSelectionIndexes(range, isSelected = true) {
- const { startKey, endKey } = range
+ const { start, end } = range
// PERF: if we're not selected, we can exit early.
if (!isSelected) {
@@ -1435,32 +1464,32 @@ class Node {
// PERF: if the start and end keys are the same, just check for the child
// that contains that single key.
- if (startKey == endKey) {
- const child = this.getFurthestAncestor(startKey)
+ if (start.key == end.key) {
+ const child = this.getFurthestAncestor(start.key)
const index = child ? this.nodes.indexOf(child) : null
return { start: index, end: index + 1 }
}
// Otherwise, check all of the children...
- let start = null
- let end = null
+ let startIndex = null
+ let endIndex = null
this.nodes.forEach((child, i) => {
if (child.object == 'text') {
- if (start == null && child.key == startKey) start = i
- if (end == null && child.key == endKey) end = i + 1
+ if (startIndex == null && child.key == start.key) startIndex = i
+ if (endIndex == null && child.key == end.key) endIndex = i + 1
} else {
- if (start == null && child.hasDescendant(startKey)) start = i
- if (end == null && child.hasDescendant(endKey)) end = i + 1
+ if (startIndex == null && child.hasDescendant(start.key)) startIndex = i
+ if (endIndex == null && child.hasDescendant(end.key)) endIndex = i + 1
}
// PERF: exit early if both start and end have been found.
- return start == null || end == null
+ return startIndex == null || endIndex == null
})
- if (isSelected && start == null) start = 0
- if (isSelected && end == null) end = this.nodes.size
- return start == null ? null : { start, end }
+ if (isSelected && startIndex == null) startIndex = 0
+ if (isSelected && endIndex == null) endIndex = this.nodes.size
+ return startIndex == null ? null : { start: startIndex, end: endIndex }
}
/**
@@ -1552,9 +1581,9 @@ class Node {
getTextsAtRange(range) {
range = this.resolveRange(range)
if (range.isUnset) return List()
- const { startKey, endKey } = range
+ const { start, end } = range
const list = new List(
- this.getTextsBetweenPositionsAsArray(startKey, endKey)
+ this.getTextsBetweenPositionsAsArray(start.key, end.key)
)
return list
@@ -1570,8 +1599,8 @@ class Node {
getTextsAtRangeAsArray(range) {
range = this.resolveRange(range)
if (range.isUnset) return []
- const { startKey, endKey } = range
- const texts = this.getTextsBetweenPositionsAsArray(startKey, endKey)
+ const { start, end } = range
+ const texts = this.getTextsBetweenPositionsAsArray(start.key, end.key)
return texts
}
@@ -2003,6 +2032,23 @@ class Node {
}
/**
+<<<<<<< HEAD
+ * Resolve a `point`, relative to the node, ensuring that the keys and
+ * offsets in the point exist and that they are synced with the paths.
+ *
+ * @param {Point|Object} point
+ * @return {Point}
+ */
+
+ resolvePoint(point) {
+ point = Point.create(point)
+ point = point.normalize(this)
+ return point
+ }
+
+ /**
+=======
+>>>>>>> master
* Resolve a `range`, relative to the node, ensuring that the keys and
* offsets in the range exist and that they are synced with the paths.
*
diff --git a/packages/slate/src/models/operation.js b/packages/slate/src/models/operation.js
index f2569e5cd..ee8c89b3d 100644
--- a/packages/slate/src/models/operation.js
+++ b/packages/slate/src/models/operation.js
@@ -289,12 +289,8 @@ class Operation extends Record(DEFAULTS) {
if (key == 'properties' && type == 'set_selection') {
const v = {}
- if ('anchorOffset' in value) v.anchorOffset = value.anchorOffset
- if ('anchorPath' in value)
- v.anchorPath = value.anchorPath && value.anchorPath.toJSON()
- if ('focusOffset' in value) v.focusOffset = value.focusOffset
- if ('focusPath' in value)
- v.focusPath = value.focusPath && value.focusPath.toJSON()
+ if ('anchor' in value) v.anchor = value.anchor.toJSON()
+ if ('focus' in value) v.focus = value.focus.toJSON()
if ('isFocused' in value) v.isFocused = value.isFocused
if ('marks' in value) v.marks = value.marks && value.marks.toJSON()
value = v
diff --git a/packages/slate/src/models/point.js b/packages/slate/src/models/point.js
new file mode 100644
index 000000000..abec6c4d5
--- /dev/null
+++ b/packages/slate/src/models/point.js
@@ -0,0 +1,426 @@
+import isPlainObject from 'is-plain-object'
+import logger from 'slate-dev-logger'
+import { Record } from 'immutable'
+
+import KeyUtils from '../utils/key-utils'
+import PathUtils from '../utils/path-utils'
+import MODEL_TYPES from '../constants/model-types'
+
+/**
+ * Default properties.
+ *
+ * @type {Object}
+ */
+
+const DEFAULTS = {
+ key: null,
+ offset: null,
+ path: null,
+}
+
+/**
+ * Point.
+ *
+ * @type {Point}
+ */
+
+class Point extends Record(DEFAULTS) {
+ /**
+ * Create a new `Point` with `attrs`.
+ *
+ * @param {Object|Point} attrs
+ * @return {Point}
+ */
+
+ static create(attrs = {}) {
+ if (Point.isPoint(attrs)) {
+ return attrs
+ }
+
+ if (isPlainObject(attrs)) {
+ return Point.fromJSON(attrs)
+ }
+
+ throw new Error(
+ `\`Point.create\` only accepts objects or points, but you passed it: ${attrs}`
+ )
+ }
+
+ /**
+ * Create a dictionary of settable point properties from `attrs`.
+ *
+ * @param {Object|Point} attrs
+ * @return {Object}
+ */
+
+ static createProperties(a = {}) {
+ if (Point.isPoint(a)) {
+ return {
+ key: a.key,
+ offset: a.offset,
+ path: a.path,
+ }
+ }
+
+ if (isPlainObject(a)) {
+ const p = {}
+ if ('key' in a) p.key = a.key
+ if ('offset' in a) p.offset = a.offset
+ if ('path' in a) p.path = PathUtils.create(a.path)
+
+ // If only a path is set, or only a key is set, ensure that the other is
+ // set to null so that it can be normalized back to the right value.
+ // Otherwise we won't realize that the path and key don't match anymore.
+ if ('path' in a && !('key' in a)) p.key = null
+ if ('key' in a && !('path' in a)) p.path = null
+
+ return p
+ }
+
+ throw new Error(
+ `\`Point.createProperties\` only accepts objects or points, but you passed it: ${a}`
+ )
+ }
+
+ /**
+ * Create a `Point` from a JSON `object`.
+ *
+ * @param {Object} object
+ * @return {Point}
+ */
+
+ static fromJSON(object) {
+ const { key = null, offset = null, path = null } = object
+
+ const point = new Point({
+ key,
+ offset,
+ path: PathUtils.create(path),
+ })
+
+ return point
+ }
+
+ /**
+ * Alias `fromJS`.
+ */
+
+ static fromJS = Point.fromJSON
+
+ /**
+ * Check if an `obj` is a `Point`.
+ *
+ * @param {Any} obj
+ * @return {Boolean}
+ */
+
+ static isPoint(obj) {
+ return !!(obj && obj[MODEL_TYPES.POINT])
+ }
+
+ /**
+ * Object.
+ *
+ * @return {String}
+ */
+
+ get object() {
+ return 'point'
+ }
+
+ /**
+ * Check whether all properties of the point are set.
+ *
+ * @return {Boolean}
+ */
+
+ get isSet() {
+ return this.key != null && this.offset != null && this.path != null
+ }
+
+ /**
+ * Check whether any property of the point is not set.
+ *
+ * @return {Boolean}
+ */
+
+ get isUnset() {
+ return !this.isSet
+ }
+
+ /**
+ * Check whether the point is at the end of a `node`.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+ isAtEndOfNode(node) {
+ if (this.isUnset) return false
+ const last = node.getLastText()
+ const is = this.key === last.key && this.offset === last.text.length
+ return is
+ }
+
+ /**
+ * Check whether the point is at the start of a `node`.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+ isAtStartOfNode(node) {
+ if (this.isUnset) return false
+
+ // PERF: Do a check for a `0` offset first since it's quickest.
+ if (this.offset != 0) return false
+
+ const first = node.getFirstText()
+ const is = this.key === first.key
+ return is
+ }
+
+ /**
+ * Check whether the point is in a `node`.
+ *
+ * @param {Node} node
+ * @return {Boolean}
+ */
+
+ isInNode(node) {
+ if (this.isUnset) return false
+ if (node.object === 'text' && node.key === this.key) return true
+ if (node.hasNode(this.key)) return true
+ return false
+ }
+
+ /**
+ * Move the point's offset backward `n` characters.
+ *
+ * @param {Number} n (optional)
+ * @return {Point}
+ */
+
+ moveBackward(n = 1) {
+ if (n === 0) return this
+ if (n < 0) return this.moveForward(-n)
+ const point = this.setOffset(this.offset - n)
+ return point
+ }
+
+ /**
+ * Move the point's offset forward `n` characters.
+ *
+ * @param {Number} n (optional)
+ * @return {Point}
+ */
+
+ moveForward(n = 1) {
+ if (n === 0) return this
+ if (n < 0) return this.moveBackward(-n)
+ const point = this.setOffset(this.offset + n)
+ return point
+ }
+
+ /**
+ * Move the point's anchor point to a new `path` and `offset`.
+ *
+ * Optionally, the `path` can be a key string, or omitted entirely in which
+ * case it would be the offset number.
+ *
+ * @param {List|String|Number} path
+ * @param {Number} offset
+ * @return {Point}
+ */
+
+ moveTo(path, offset = 0) {
+ let key = this.key
+
+ if (typeof path === 'number') {
+ offset = path
+ path = this.path
+ } else if (typeof path === 'string') {
+ key = path
+ path = key === this.key ? this.path : null
+ } else {
+ key = path.equals(this.path) ? this.key : null
+ }
+
+ const point = this.merge({ key, path, offset })
+ return point
+ }
+
+ /**
+ * Move the point's anchor point to the start of a `node`.
+ *
+ * @param {Node} node
+ * @return {Point}
+ */
+
+ moveToStartOfNode(node) {
+ const first = node.getFirstText()
+ const point = this.moveTo(first.key, 0)
+ return point
+ }
+
+ /**
+ * Move the point's anchor point to the end of a `node`.
+ *
+ * @param {Node} node
+ * @return {Point}
+ */
+
+ moveToEndOfNode(node) {
+ const last = node.getLastText()
+ const point = this.moveTo(last.key, last.text.length)
+ return point
+ }
+
+ /**
+ * Normalize the point relative to a `node`, ensuring that its key and path
+ * reference a text node, or that it gets unset.
+ *
+ * @param {Node} node
+ * @return {Point}
+ */
+
+ normalize(node) {
+ // If both the key and path are null, there's no reference to a node, so
+ // make sure it is entirely unset.
+ if (this.key == null && this.path == null) {
+ return this.setOffset(null)
+ }
+
+ const { key, offset, path } = this
+ const target = node.getNode(key || path)
+
+ if (!target) {
+ logger.warn("A point's `path` or `key` invalid and was reset:", this)
+
+ const text = node.getFirstText()
+ if (!text) return Point.create()
+
+ const point = this.merge({
+ key: text.key,
+ offset: 0,
+ path: node.getPath(text.key),
+ })
+
+ return point
+ }
+
+ if (target.object !== 'text') {
+ logger.warn('A point should not reference a non-text node:', target)
+
+ const text = target.getTextAtOffset(offset)
+ const before = target.getOffset(text.key)
+ const point = this.merge({
+ offset: offset - before,
+ key: text.key,
+ path: node.getPath(text.key),
+ })
+
+ return point
+ }
+
+ if (target && path && key && key !== target.key) {
+ logger.warn("A point's `key` did not match its `path`:", this, target)
+ }
+
+ const point = this.merge({
+ key: target.key,
+ path: path == null ? node.getPath(target.key) : path,
+ offset: offset == null ? 0 : Math.min(offset, target.text.length),
+ })
+
+ return point
+ }
+
+ /**
+ * Set the point's key to a new `key`.
+ *
+ * @param {String} key
+ * @return {Point}
+ */
+
+ setKey(key) {
+ if (key !== null) {
+ key = KeyUtils.create(key)
+ }
+
+ const point = this.set('key', key)
+ return point
+ }
+
+ /**
+ * Set the point's offset to a new `offset`.
+ *
+ * @param {Number} offset
+ * @return {Point}
+ */
+
+ setOffset(offset) {
+ const point = this.set('offset', offset)
+ return point
+ }
+
+ /**
+ * Set the point's path to a new `path`.
+ *
+ * @param {List|Array} path
+ * @return {Point}
+ */
+
+ setPath(path) {
+ if (path !== null) {
+ path = PathUtils.create(path)
+ }
+
+ const point = this.set('path', path)
+ return point
+ }
+
+ /**
+ * Return a JSON representation of the point.
+ *
+ * @param {Object} options
+ * @return {Object}
+ */
+
+ toJSON(options = {}) {
+ const object = {
+ object: this.object,
+ key: this.key,
+ offset: this.offset,
+ path: this.path && this.path.toArray(),
+ }
+
+ if (!options.preserveKeys) {
+ delete object.key
+ }
+
+ return object
+ }
+
+ /**
+ * Alias `toJS`.
+ */
+
+ toJS() {
+ return this.toJSON()
+ }
+}
+
+/**
+ * Attach a pseudo-symbol for type checking.
+ */
+
+Point.prototype[MODEL_TYPES.POINT] = true
+
+/**
+ * Export.
+ *
+ * @type {Point}
+ */
+
+export default Point
diff --git a/packages/slate/src/models/range.js b/packages/slate/src/models/range.js
index 02094f0c5..11e3a5cce 100644
--- a/packages/slate/src/models/range.js
+++ b/packages/slate/src/models/range.js
@@ -5,6 +5,7 @@ import { List, Record, Set } from 'immutable'
import PathUtils from '../utils/path-utils'
import MODEL_TYPES from '../constants/model-types'
import Mark from './mark'
+import Point from './point'
/**
* Default properties.
@@ -13,12 +14,8 @@ import Mark from './mark'
*/
const DEFAULTS = {
- anchorKey: null,
- anchorOffset: null,
- anchorPath: null,
- focusKey: null,
- focusOffset: null,
- focusPath: null,
+ anchor: Point.create(),
+ focus: Point.create(),
isAtomic: false,
isFocused: false,
marks: null,
@@ -80,12 +77,8 @@ class Range extends Record(DEFAULTS) {
static createProperties(a = {}) {
if (Range.isRange(a)) {
return {
- anchorKey: a.anchorKey,
- anchorOffset: a.anchorOffset,
- anchorPath: a.anchorPath,
- focusKey: a.focusKey,
- focusOffset: a.focusOffset,
- focusPath: a.focusPath,
+ anchor: Point.createProperties(a.anchor),
+ focus: Point.createProperties(a.focus),
isAtomic: a.isAtomic,
isFocused: a.isFocused,
marks: a.marks,
@@ -94,25 +87,12 @@ class Range extends Record(DEFAULTS) {
if (isPlainObject(a)) {
const p = {}
- if ('anchorKey' in a) p.anchorKey = a.anchorKey
- if ('anchorOffset' in a) p.anchorOffset = a.anchorOffset
- if ('anchorPath' in a) p.anchorPath = PathUtils.create(a.anchorPath)
- if ('focusKey' in a) p.focusKey = a.focusKey
- if ('focusOffset' in a) p.focusOffset = a.focusOffset
- if ('focusPath' in a) p.focusPath = PathUtils.create(a.focusPath)
+ if ('anchor' in a) p.anchor = Point.create(a.anchor)
+ if ('focus' in a) p.focus = Point.create(a.focus)
if ('isAtomic' in a) p.isAtomic = a.isAtomic
if ('isFocused' in a) p.isFocused = a.isFocused
if ('marks' in a)
p.marks = a.marks == null ? null : Mark.createSet(a.marks)
-
- // If only a path is set, or only a key is set, ensure that the other is
- // set to null so that it can be normalized back to the right value.
- // Otherwise we won't realize that the path and key don't match anymore.
- if ('anchorPath' in a && !('anchorKey' in a)) p.anchorKey = null
- if ('anchorKey' in a && !('anchorPath' in a)) p.anchorPath = null
- if ('focusPath' in a && !('focusKey' in a)) p.focusKey = null
- if ('focusKey' in a && !('focusPath' in a)) p.focusPath = null
-
return p
}
@@ -129,25 +109,48 @@ class Range extends Record(DEFAULTS) {
*/
static fromJSON(object) {
- const {
- anchorKey = null,
- anchorOffset = null,
- anchorPath = null,
- focusKey = null,
- focusOffset = null,
- focusPath = null,
+ let {
+ anchor,
+ focus,
isAtomic = false,
isFocused = false,
marks = null,
} = object
+ if (
+ !anchor &&
+ (object.anchorKey || object.anchorOffset || object.anchorPath)
+ ) {
+ logger.deprecate(
+ '0.37.0',
+ '`Range` objects now take a `Point` object as an `anchor` instead of taking `anchorKey/Offset/Path` properties. But you passed:',
+ object
+ )
+
+ anchor = {
+ key: object.anchorKey,
+ offset: object.anchorOffset,
+ path: object.anchorPath,
+ }
+ }
+
+ if (!focus && (object.focusKey || object.focusOffset || object.focusPath)) {
+ logger.deprecate(
+ '0.37.0',
+ '`Range` objects now take a `Point` object as a `focus` instead of taking `focusKey/Offset/Path` properties. But you passed:',
+ object
+ )
+
+ focus = {
+ key: object.focusKey,
+ offset: object.focusOffset,
+ path: object.focusPath,
+ }
+ }
+
const range = new Range({
- anchorKey,
- anchorOffset,
- anchorPath: PathUtils.create(anchorPath),
- focusKey,
- focusOffset,
- focusPath: PathUtils.create(focusPath),
+ anchor: Point.fromJSON(anchor || {}),
+ focus: Point.fromJSON(focus || {}),
isAtomic,
isFocused,
marks: marks == null ? null : new Set(marks.map(Mark.fromJSON)),
@@ -209,7 +212,8 @@ class Range extends Record(DEFAULTS) {
get isCollapsed() {
return (
- this.anchorKey == this.focusKey && this.anchorOffset == this.focusOffset
+ this.anchor.key === this.focus.key &&
+ this.anchor.offset === this.focus.offset
)
}
@@ -230,14 +234,17 @@ class Range extends Record(DEFAULTS) {
*/
get isBackward() {
- if (this.isUnset) return null
+ const { isUnset, anchor, focus } = this
- // PERF: if the two keys are the same, we can just use the offsets.
- if (this.anchorKey === this.focusKey) {
- return this.anchorOffset > this.focusOffset
+ if (isUnset) {
+ return null
}
- const isBackward = PathUtils.isBefore(this.focusPath, this.anchorPath)
+ if (anchor.key === focus.key) {
+ return anchor.offset > focus.offset
+ }
+
+ const isBackward = PathUtils.isBefore(focus.path, anchor.path)
return isBackward
}
@@ -249,7 +256,8 @@ class Range extends Record(DEFAULTS) {
get isForward() {
const { isBackward } = this
- return isBackward == null ? null : !isBackward
+ const isForward = isBackward == null ? null : !isBackward
+ return isForward
}
/**
@@ -259,14 +267,9 @@ class Range extends Record(DEFAULTS) {
*/
get isUnset() {
- return (
- this.anchorKey == null ||
- this.anchorOffset == null ||
- this.anchorPath == null ||
- this.focusKey == null ||
- this.focusOffset == null ||
- this.focusPath == null
- )
+ const { anchor, focus } = this
+ const isUnset = anchor.isUnset || focus.isUnset
+ return isUnset
}
/**
@@ -280,240 +283,23 @@ class Range extends Record(DEFAULTS) {
}
/**
- * Get the start key.
+ * Get the start point.
*
* @return {String}
*/
- get startKey() {
- return this.isBackward ? this.focusKey : this.anchorKey
+ get start() {
+ return this.isBackward ? this.focus : this.anchor
}
/**
- * Get the start offset.
+ * Get the end point.
*
* @return {String}
*/
- get startOffset() {
- return this.isBackward ? this.focusOffset : this.anchorOffset
- }
-
- /**
- * Get the start path.
- *
- * @return {String}
- */
-
- get startPath() {
- return this.isBackward ? this.focusPath : this.anchorPath
- }
-
- /**
- * Get the end key.
- *
- * @return {String}
- */
-
- get endKey() {
- return this.isBackward ? this.anchorKey : this.focusKey
- }
-
- /**
- * Get the end offset.
- *
- * @return {String}
- */
-
- get endOffset() {
- return this.isBackward ? this.anchorOffset : this.focusOffset
- }
-
- /**
- * Get the end path.
- *
- * @return {String}
- */
-
- get endPath() {
- return this.isBackward ? this.anchorPath : this.focusPath
- }
-
- /**
- * Check whether anchor point of the range is at the start of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasAnchorAtStartOf(node) {
- // PERF: Do a check for a `0` offset first since it's quickest.
- if (this.anchorOffset != 0) return false
- const first = getFirstText(node)
- return this.anchorKey == first.key
- }
-
- /**
- * Check whether anchor point of the range is at the end of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasAnchorAtEndOf(node) {
- const last = getLastText(node)
- return this.anchorKey == last.key && this.anchorOffset == last.text.length
- }
-
- /**
- * Check whether the anchor edge of a range is in a `node` and at an
- * offset between `start` and `end`.
- *
- * @param {Node} node
- * @param {Number} start
- * @param {Number} end
- * @return {Boolean}
- */
-
- hasAnchorBetween(node, start, end) {
- return (
- this.anchorOffset <= end &&
- start <= this.anchorOffset &&
- this.hasAnchorIn(node)
- )
- }
-
- /**
- * Check whether the anchor edge of a range is in a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasAnchorIn(node) {
- return node.object == 'text'
- ? node.key == this.anchorKey
- : this.anchorKey != null && node.hasDescendant(this.anchorKey)
- }
-
- /**
- * Check whether focus point of the range is at the end of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasFocusAtEndOf(node) {
- const last = getLastText(node)
- return this.focusKey == last.key && this.focusOffset == last.text.length
- }
-
- /**
- * Check whether focus point of the range is at the start of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasFocusAtStartOf(node) {
- if (this.focusOffset !== 0) return false
- const first = getFirstText(node)
- return this.focusKey == first.key
- }
-
- /**
- * Check whether the focus edge of a range is in a `node` and at an
- * offset between `start` and `end`.
- *
- * @param {Node} node
- * @param {Number} start
- * @param {Number} end
- * @return {Boolean}
- */
-
- hasFocusBetween(node, start, end) {
- return (
- start <= this.focusOffset &&
- this.focusOffset <= end &&
- this.hasFocusIn(node)
- )
- }
-
- /**
- * Check whether the focus edge of a range is in a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- hasFocusIn(node) {
- return node.object == 'text'
- ? node.key == this.focusKey
- : this.focusKey != null && node.hasDescendant(this.focusKey)
- }
-
- /**
- * Check whether the range is at the start of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- isAtStartOf(node) {
- return this.isCollapsed && this.hasAnchorAtStartOf(node)
- }
-
- /**
- * Check whether the range is at the end of a `node`.
- *
- * @param {Node} node
- * @return {Boolean}
- */
-
- isAtEndOf(node) {
- return this.isCollapsed && this.hasAnchorAtEndOf(node)
- }
-
- /**
- * Focus the range.
- *
- * @return {Range}
- */
-
- focus() {
- return this.merge({
- isFocused: true,
- })
- }
-
- /**
- * Blur the range.
- *
- * @return {Range}
- */
-
- blur() {
- return this.merge({
- isFocused: false,
- })
- }
-
- /**
- * Unset the range.
- *
- * @return {Range}
- */
-
- deselect() {
- return this.merge({
- anchorKey: null,
- anchorOffset: null,
- anchorPath: null,
- focusKey: null,
- focusOffset: null,
- focusPath: null,
- isFocused: false,
- })
+ get end() {
+ return this.isBackward ? this.anchor : this.focus
}
/**
@@ -523,154 +309,80 @@ class Range extends Record(DEFAULTS) {
*/
flip() {
- return this.merge({
- anchorKey: this.focusKey,
- anchorOffset: this.focusOffset,
- anchorPath: this.focusPath,
- focusKey: this.anchorKey,
- focusOffset: this.anchorOffset,
- focusPath: this.anchorPath,
- })
+ const range = this.setPoints([this.focus, this.anchor])
+ return range
}
/**
- * Move the anchor offset `n` characters.
+ * Move the anchor and focus offsets forward `n` characters.
*
- * @param {Number} n (optional)
+ * @param {Number} n
* @return {Range}
*/
- moveAnchor(n = 1) {
- const anchorOffset = this.anchorOffset + n
- return this.merge({ anchorOffset })
+ moveForward(n) {
+ const range = this.setPoints([
+ this.anchor.moveForward(n),
+ this.focus.moveForward(n),
+ ])
+
+ return range
}
/**
- * Move the anchor offset `n` characters.
+ * Move the anchor and focus offsets backward `n` characters.
*
- * @param {Number} n (optional)
+ * @param {Number} n
* @return {Range}
*/
- moveFocus(n = 1) {
- const focusOffset = this.focusOffset + n
- return this.merge({ focusOffset })
+ moveBackward(n) {
+ const range = this.setPoints([
+ this.anchor.moveBackward(n),
+ this.focus.moveBackward(n),
+ ])
+
+ return range
}
/**
- * Move the range's anchor point to a new `key` or `path` and `offset`.
+ * Move the anchor offset backward `n` characters.
*
- * @param {String|List} key or path
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveAnchorBackward(n) {
+ const range = this.setAnchor(this.anchor.moveBackward(n))
+ return range
+ }
+
+ /**
+ * Move the anchor offset forward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveAnchorForward(n) {
+ const range = this.setAnchor(this.anchor.moveForward(n))
+ return range
+ }
+
+ /**
+ * Move the range's anchor point to a new `path` and `offset`.
+ *
+ * Optionally, the `path` can be a key string, or omitted entirely in which
+ * case it would be the offset number.
+ *
+ * @param {List|String} path
* @param {Number} offset
* @return {Range}
*/
- moveAnchorTo(key, offset) {
- const { anchorKey, focusKey, anchorPath, focusPath } = this
-
- if (typeof key === 'string') {
- const isAnchor = key === anchorKey
- const isFocus = key === focusKey
- return this.merge({
- anchorKey: key,
- anchorPath: isFocus ? focusPath : isAnchor ? anchorPath : null,
- anchorOffset: offset,
- })
- } else {
- const path = key
- const isAnchor = path && path.equals(anchorPath)
- const isFocus = path && path.equals(focusPath)
- return this.merge({
- anchorPath: path,
- anchorKey: isAnchor ? anchorKey : isFocus ? focusKey : null,
- anchorOffset: offset,
- })
- }
- }
-
- /**
- * Move the range's focus point to a new `key` or `path` and `offset`.
- *
- * @param {String|List} key or path
- * @param {Number} offset
- * @return {Range}
- */
-
- moveFocusTo(key, offset) {
- const { focusKey, anchorKey, anchorPath, focusPath } = this
-
- if (typeof key === 'string') {
- const isAnchor = key === anchorKey
- const isFocus = key === focusKey
- return this.merge({
- focusKey: key,
- focusPath: isAnchor ? anchorPath : isFocus ? focusPath : null,
- focusOffset: offset,
- })
- } else {
- const path = key
- const isAnchor = path && path.equals(anchorPath)
- const isFocus = path && path.equals(focusPath)
- return this.merge({
- focusPath: path,
- focusKey: isFocus ? focusKey : isAnchor ? anchorKey : null,
- focusOffset: offset,
- })
- }
- }
-
- /**
- * Move the range to `anchorOffset`.
- *
- * @param {Number} anchorOffset
- * @return {Range}
- */
-
- moveAnchorOffsetTo(anchorOffset) {
- return this.merge({ anchorOffset })
- }
-
- /**
- * Move the range to `focusOffset`.
- *
- * @param {Number} focusOffset
- * @return {Range}
- */
-
- moveFocusOffsetTo(focusOffset) {
- return this.merge({ focusOffset })
- }
-
- /**
- * Move the range to `anchorOffset` and `focusOffset`.
- *
- * @param {Number} anchorOffset
- * @param {Number} focusOffset (optional)
- * @return {Range}
- */
-
- moveOffsetsTo(anchorOffset, focusOffset = anchorOffset) {
- return this.moveAnchorOffsetTo(anchorOffset).moveFocusOffsetTo(focusOffset)
- }
-
- /**
- * Move the focus point to the anchor point.
- *
- * @return {Range}
- */
-
- moveToAnchor() {
- return this.moveFocusTo(this.anchorKey, this.anchorOffset)
- }
-
- /**
- * Move the anchor point to the focus point.
- *
- * @return {Range}
- */
-
- moveToFocus() {
- return this.moveAnchorTo(this.focusKey, this.focusOffset)
+ moveAnchorTo(path, offset) {
+ const range = this.setAnchor(this.anchor.moveTo(path, offset))
+ return range
}
/**
@@ -680,9 +392,9 @@ class Range extends Record(DEFAULTS) {
* @return {Range}
*/
- moveAnchorToStartOf(node) {
- node = getFirstText(node)
- return this.moveAnchorTo(node.key, 0)
+ moveAnchorToStartOfNode(node) {
+ const range = this.setAnchor(this.anchor.moveToStartOfNode(node))
+ return range
}
/**
@@ -692,9 +404,113 @@ class Range extends Record(DEFAULTS) {
* @return {Range}
*/
- moveAnchorToEndOf(node) {
- node = getLastText(node)
- return this.moveAnchorTo(node.key, node.text.length)
+ moveAnchorToEndOfNode(node) {
+ const range = this.setAnchor(this.anchor.moveToEndOfNode(node))
+ return range
+ }
+
+ /**
+ * Move the end offset backward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveEndBackward(n) {
+ const range = this.setEnd(this.end.moveBackward(n))
+ return range
+ }
+
+ /**
+ * Move the end offset forward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveEndForward(n) {
+ const range = this.setEnd(this.end.moveForward(n))
+ return range
+ }
+
+ /**
+ * Move the range's end point to a new `path` and `offset`.
+ *
+ * Optionally, the `path` can be a key string, or omitted entirely in which
+ * case it would be the offset number.
+ *
+ * @param {List|String} path
+ * @param {Number} offset
+ * @return {Range}
+ */
+
+ moveEndTo(path, offset) {
+ const range = this.setEnd(this.end.moveTo(path, offset))
+ return range
+ }
+
+ /**
+ * Move the range's end point to the start of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveEndToStartOfNode(node) {
+ const range = this.setEnd(this.end.moveToStartOfNode(node))
+ return range
+ }
+
+ /**
+ * Move the range's end point to the end of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveEndToEndOfNode(node) {
+ const range = this.setEnd(this.end.moveToEndOfNode(node))
+ return range
+ }
+
+ /**
+ * Move the focus offset backward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveFocusBackward(n) {
+ const range = this.setFocus(this.focus.moveBackward(n))
+ return range
+ }
+
+ /**
+ * Move the focus offset forward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveFocusForward(n) {
+ const range = this.setFocus(this.focus.moveForward(n))
+ return range
+ }
+
+ /**
+ * Move the range's focus point to a new `path` and `offset`.
+ *
+ * Optionally, the `path` can be a key string, or omitted entirely in which
+ * case it would be the offset number.
+ *
+ * @param {List|String} path
+ * @param {Number} offset
+ * @return {Range}
+ */
+
+ moveFocusTo(path, offset) {
+ const range = this.setFocus(this.focus.moveTo(path, offset))
+ return range
}
/**
@@ -704,9 +520,9 @@ class Range extends Record(DEFAULTS) {
* @return {Range}
*/
- moveFocusToStartOf(node) {
- node = getFirstText(node)
- return this.moveFocusTo(node.key, 0)
+ moveFocusToStartOfNode(node) {
+ const range = this.setFocus(this.focus.moveToStartOfNode(node))
+ return range
}
/**
@@ -716,9 +532,138 @@ class Range extends Record(DEFAULTS) {
* @return {Range}
*/
- moveFocusToEndOf(node) {
- node = getLastText(node)
- return this.moveFocusTo(node.key, node.text.length)
+ moveFocusToEndOfNode(node) {
+ const range = this.setFocus(this.focus.moveToEndOfNode(node))
+ return range
+ }
+
+ /**
+ * Move the start offset backward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveStartBackward(n) {
+ const range = this.setStart(this.start.moveBackward(n))
+ return range
+ }
+
+ /**
+ * Move the start offset forward `n` characters.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveStartForward(n) {
+ const range = this.setStart(this.start.moveForward(n))
+ return range
+ }
+
+ /**
+ * Move the range's start point to a new `path` and `offset`.
+ *
+ * Optionally, the `path` can be a key string, or omitted entirely in which
+ * case it would be the offset number.
+ *
+ * @param {List|String} path
+ * @param {Number} offset
+ * @return {Range}
+ */
+
+ moveStartTo(path, offset) {
+ const range = this.setStart(this.start.moveTo(path, offset))
+ return range
+ }
+
+ /**
+ * Move the range's start point to the start of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveStartToStartOfNode(node) {
+ const range = this.setStart(this.start.moveToStartOfNode(node))
+ return range
+ }
+
+ /**
+ * Move the range's start point to the end of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveStartToEndOfNode(node) {
+ const range = this.setStart(this.start.moveToEndOfNode(node))
+ return range
+ }
+
+ /**
+ * Move range's points to a new `path` and `offset`.
+ *
+ * @param {Number} n
+ * @return {Range}
+ */
+
+ moveTo(path, offset) {
+ const range = this.setPoints([
+ this.anchor.moveTo(path, offset),
+ this.focus.moveTo(path, offset),
+ ])
+
+ return range
+ }
+
+ /**
+ * Move the focus point to the anchor point.
+ *
+ * @return {Range}
+ */
+
+ moveToAnchor() {
+ const range = this.setFocus(this.anchor)
+ return range
+ }
+
+ /**
+ * Move the start point to the end point.
+ *
+ * @return {Range}
+ */
+
+ moveToEnd() {
+ const range = this.setStart(this.end)
+ return range
+ }
+
+ /**
+ * Move the range's points to the end of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveToEndOfNode(node) {
+ const range = this.setPoints([
+ this.anchor.moveToEndOfNode(node),
+ this.focus.moveToEndOfNode(node),
+ ])
+
+ return range
+ }
+
+ /**
+ * Move the anchor point to the focus point.
+ *
+ * @return {Range}
+ */
+
+ moveToFocus() {
+ const range = this.setAnchor(this.focus)
+ return range
}
/**
@@ -729,8 +674,39 @@ class Range extends Record(DEFAULTS) {
* @return {Range}
*/
- moveToRangeOf(start, end = start) {
- const range = this.moveAnchorToStartOf(start).moveFocusToEndOf(end)
+ moveToRangeOfNode(start, end = start) {
+ const range = this.setPoints([
+ this.anchor.moveToStartOfNode(start),
+ this.focus.moveToEndOfNode(end),
+ ])
+
+ return range
+ }
+
+ /**
+ * Move the end point to the start point.
+ *
+ * @return {Range}
+ */
+
+ moveToStart() {
+ const range = this.setEnd(this.start)
+ return range
+ }
+
+ /**
+ * Move the range's points to the start of a `node`.
+ *
+ * @param {Node} node
+ * @return {Range}
+ */
+
+ moveToStartOfNode(node) {
+ const range = this.setPoints([
+ this.anchor.moveToStartOfNode(node),
+ this.focus.moveToStartOfNode(node),
+ ])
+
return range
}
@@ -743,89 +719,96 @@ class Range extends Record(DEFAULTS) {
*/
normalize(node) {
- const range = this
- let {
- anchorKey,
- anchorOffset,
- anchorPath,
- focusKey,
- focusOffset,
- focusPath,
- } = range
+ const range = this.setPoints([
+ this.anchor.normalize(node),
+ this.focus.normalize(node),
+ ])
- // If either point in the range is unset, make sure it is fully unset.
- if (
- (anchorKey == null && anchorPath == null) ||
- (focusKey == null && focusPath == null) ||
- anchorOffset == null ||
- focusOffset == null
- ) {
- return Range.create()
+ return range
+ }
+
+ /**
+ * Set the anchor point to a new `anchor`.
+ *
+ * @param {Point} anchor
+ * @return {Range}
+ */
+
+ setAnchor(anchor) {
+ const range = this.set('anchor', anchor)
+ return range
+ }
+
+ /**
+ * Set the end point to a new `point`.
+ *
+ * @param {Point} point
+ * @return {Range}
+ */
+
+ setEnd(point) {
+ const range = this.isBackward ? this.setAnchor(point) : this.setFocus(point)
+ return range
+ }
+
+ /**
+ * Set the focus point to a new `focus`.
+ *
+ * @param {Point} focus
+ * @return {Range}
+ */
+
+ setFocus(focus) {
+ const range = this.set('focus', focus)
+ return range
+ }
+
+ /**
+ * Set the anchor and focus points to new `values`.
+ *
+ * @param {Array} values
+ * @return {Range}
+ */
+
+ setPoints(values) {
+ const [anchor, focus] = values
+ const range = this.set('anchor', anchor).set('focus', focus)
+ return range
+ }
+
+ /**
+ * Set the start point to a new `point`.
+ *
+ * @param {Point} point
+ * @return {Range}
+ */
+
+ setStart(point) {
+ const range = this.isBackward ? this.setFocus(point) : this.setAnchor(point)
+ return range
+ }
+
+ /**
+ * Set new `properties` on the range.
+ *
+ * @param {Object|Range} properties
+ * @return {Range}
+ */
+
+ setProperties(properties) {
+ properties = Range.createProperties(properties)
+ const { anchor, focus, ...props } = properties
+
+ if (anchor) {
+ props.anchor = Point.create(anchor)
}
- // Get the anchor and focus nodes.
- let anchorNode = node.getNode(anchorKey || anchorPath)
- let focusNode = node.getNode(focusKey || focusPath)
-
- // If the range is malformed, warn and zero it out.
- if (!anchorNode || !focusNode) {
- logger.warn(
- 'The range was invalid and was reset. The range in question was:',
- range
- )
-
- const first = node.getFirstText()
- const path = first && node.getPath(first.key)
- return range.merge({
- anchorKey: first ? first.key : null,
- anchorOffset: first ? 0 : null,
- anchorPath: first ? path : null,
- focusKey: first ? first.key : null,
- focusOffset: first ? 0 : null,
- focusPath: first ? path : null,
- })
+ if (focus) {
+ props.focus = Point.create(focus)
}
- // If the anchor node isn't a text node, match it to one.
- if (anchorNode.object != 'text') {
- logger.warn(
- 'The range anchor was set to a Node that is not a Text node. This should not happen and can degrade performance. The node in question was:',
- anchorNode
- )
-
- const anchorText = anchorNode.getTextAtOffset(anchorOffset)
- const offset = anchorNode.getOffset(anchorText.key)
- anchorOffset = anchorOffset - offset
- anchorNode = anchorText
- }
-
- // If the focus node isn't a text node, match it to one.
- if (focusNode.object != 'text') {
- logger.warn(
- 'The range focus was set to a Node that is not a Text node. This should not happen and can degrade performance. The node in question was:',
- focusNode
- )
-
- const focusText = focusNode.getTextAtOffset(focusOffset)
- const offset = focusNode.getOffset(focusText.key)
- focusOffset = focusOffset - offset
- focusNode = focusText
- }
-
- anchorKey = anchorNode.key
- focusKey = focusNode.key
- anchorPath = node.getPath(anchorKey)
- focusPath = node.getPath(focusKey)
-
- // Merge in any updated properties.
- return range.merge({
- anchorKey,
- anchorOffset,
- anchorPath,
- focusKey,
- focusOffset,
- focusPath,
- })
+ const range = this.merge(props)
+ return range
}
/**
@@ -838,23 +821,14 @@ class Range extends Record(DEFAULTS) {
toJSON(options = {}) {
const object = {
object: this.object,
- anchorKey: this.anchorKey,
- anchorOffset: this.anchorOffset,
- anchorPath: this.anchorPath && this.anchorPath.toArray(),
- focusKey: this.focusKey,
- focusOffset: this.focusOffset,
- focusPath: this.focusPath && this.focusPath.toArray(),
+ anchor: this.anchor.toJSON(options),
+ focus: this.focus.toJSON(options),
isAtomic: this.isAtomic,
isFocused: this.isFocused,
marks:
this.marks == null ? null : this.marks.toArray().map(m => m.toJSON()),
}
- if (!options.preserveKeys) {
- delete object.anchorKey
- delete object.focusKey
- }
-
return object
}
@@ -865,6 +839,546 @@ class Range extends Record(DEFAULTS) {
toJS() {
return this.toJSON()
}
+
+ /**
+ * Deprecated.
+ */
+
+ get anchorKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.anchorKey` property has been deprecated, use `range.anchor.key` instead.'
+ )
+
+ return this.anchor.key
+ }
+
+ get anchorOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.anchorOffset` property has been deprecated, use `range.anchor.offset` instead.'
+ )
+
+ return this.anchor.offset
+ }
+
+ get anchorPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.anchorPath` property has been deprecated, use `range.anchor.path` instead.'
+ )
+
+ return this.anchor.path
+ }
+
+ get focusKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.focusKey` property has been deprecated, use `range.focus.key` instead.'
+ )
+
+ return this.focus.key
+ }
+
+ get focusOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.focusOffset` property has been deprecated, use `range.focus.offset` instead.'
+ )
+
+ return this.focus.offset
+ }
+
+ get focusPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.focusPath` property has been deprecated, use `range.focus.path` instead.'
+ )
+
+ return this.focus.path
+ }
+
+ get startKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.startKey` property has been deprecated, use `range.start.key` instead.'
+ )
+
+ return this.start.key
+ }
+
+ get startOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.startOffset` property has been deprecated, use `range.start.offset` instead.'
+ )
+
+ return this.start.offset
+ }
+
+ get startPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.startPath` property has been deprecated, use `range.start.path` instead.'
+ )
+
+ return this.start.path
+ }
+
+ get endKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.endKey` property has been deprecated, use `range.end.key` instead.'
+ )
+
+ return this.end.key
+ }
+
+ get endOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.endOffset` property has been deprecated, use `range.end.offset` instead.'
+ )
+
+ return this.end.offset
+ }
+
+ get endPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `range.endPath` property has been deprecated, use `range.end.path` instead.'
+ )
+
+ return this.end.path
+ }
+
+ hasAnchorAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasAnchorAtStartOf` method is deprecated, please use `Range.anchor.isAtStartOfNode` instead.'
+ )
+
+ return this.anchor.isAtStartOfNode(node)
+ }
+
+ hasAnchorAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasAnchorAtEndOf` method is deprecated, please use `Range.anchor.isAtEndOfNode` instead.'
+ )
+
+ return this.anchor.isAtEndOfNode(node)
+ }
+
+ hasAnchorBetween(node, start, end) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasAnchorBetween` method is deprecated, please use the `Range.anchor` methods and properties directly instead.'
+ )
+
+ return (
+ this.anchor.offset <= end &&
+ start <= this.anchor.offset &&
+ this.anchor.isInNode(node)
+ )
+ }
+
+ hasAnchorIn(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasAnchorAtEndOf` method is deprecated, please use `Range.anchor.isInNode` instead.'
+ )
+
+ return this.anchor.isInNode(node)
+ }
+
+ hasEdgeAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEdgeAtStartOf` method is deprecated.'
+ )
+
+ return this.anchor.isAtStartOfNode(node) || this.focus.isAtStartOfNode(node)
+ }
+
+ hasEdgeAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEdgeAtEndOf` method is deprecated.'
+ )
+
+ return this.anchor.isAtEndOfNode(node) || this.focus.isAtEndOfNode(node)
+ }
+
+ hasEdgeBetween(node, start, end) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEdgeBetween` method is deprecated.'
+ )
+
+ return (
+ (this.anchor.offset <= end &&
+ start <= this.anchor.offset &&
+ this.anchor.isInNode(node)) ||
+ (this.focus.offset <= end &&
+ start <= this.focus.offset &&
+ this.focus.isInNode(node))
+ )
+ }
+
+ hasEdgeIn(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEdgeAtEndOf` method is deprecated.'
+ )
+
+ return this.anchor.isInNode(node) || this.focus.isInNode(node)
+ }
+
+ hasEndAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEndAtStartOf` method is deprecated, please use `Range.end.isAtStartOfNode` instead.'
+ )
+
+ return this.end.isAtStartOfNode(node)
+ }
+
+ hasEndAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEndAtEndOf` method is deprecated, please use `Range.end.isAtEndOfNode` instead.'
+ )
+
+ return this.end.isAtEndOfNode(node)
+ }
+
+ hasEndBetween(node, start, end) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEndBetween` method is deprecated, please use the `Range.end` methods and properties directly instead.'
+ )
+
+ return (
+ this.end.offset <= end &&
+ start <= this.end.offset &&
+ this.end.isInNode(node)
+ )
+ }
+
+ hasEndIn(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasEndAtEndOf` method is deprecated, please use `Range.end.isInNode` instead.'
+ )
+
+ return this.end.isInNode(node)
+ }
+
+ hasFocusAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasFocusAtEndOf` method is deprecated, please use `Range.focus.isAtEndOfNode` instead.'
+ )
+
+ return this.focus.isAtEndOfNode(node)
+ }
+
+ hasFocusAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasFocusAtStartOf` method is deprecated, please use `Range.focus.isAtStartOfNode` instead.'
+ )
+
+ return this.focus.isAtStartOfNode(node)
+ }
+
+ hasFocusBetween(node, start, end) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasFocusBetween` method is deprecated, please use the `Range.focus` methods and properties directly instead.'
+ )
+
+ return (
+ start <= this.focus.offset &&
+ this.focus.offset <= end &&
+ this.focus.isInNode(node)
+ )
+ }
+
+ hasFocusIn(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasFocusAtEndOf` method is deprecated, please use `Range.focus.isInNode` instead.'
+ )
+
+ return this.focus.isInNode(node)
+ }
+
+ hasStartAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasStartAtStartOf` method is deprecated, please use `Range.start.isAtStartOfNode` instead.'
+ )
+
+ return this.start.isAtStartOfNode(node)
+ }
+
+ hasStartAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasStartAtEndOf` method is deprecated, please use `Range.start.isAtEndOfNode` instead.'
+ )
+
+ return this.start.isAtEndOfNode(node)
+ }
+
+ hasStartBetween(node, start, end) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasStartBetween` method is deprecated, please use the `Range.start` methods and properties directly instead.'
+ )
+
+ return (
+ this.start.offset <= end &&
+ start <= this.start.offset &&
+ this.start.isInNode(node)
+ )
+ }
+
+ hasStartIn(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.hasStartAtEndOf` method is deprecated, please use `Range.start.isInNode` instead.'
+ )
+
+ return this.start.isInNode(node)
+ }
+
+ isAtStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.isAtStartOf` method is deprecated, please use `Range.isCollapsed` and `Point.isAtStartOfNode` instead.'
+ )
+
+ return this.isCollapsed && this.anchor.isAtStartOfNode(node)
+ }
+
+ isAtEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.isAtEndOf` method is deprecated, please use `Range.isCollapsed` and `Point.isAtEndOfNode` instead.'
+ )
+
+ return this.isCollapsed && this.anchor.isAtEndOfNode(node)
+ }
+
+ blur() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.blur` method is deprecated, please use `Range.merge` directly instead.'
+ )
+
+ return this.merge({ isFocused: false })
+ }
+
+ deselect() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.deselect` method is deprecated, please use `Range.create` to create a new unset range instead.'
+ )
+
+ return Range.create()
+ }
+
+ moveAnchorOffsetTo(o) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveAnchorOffsetTo` method is deprecated, please use `Range.moveAnchorTo(offset)` instead.'
+ )
+
+ return this.moveAnchorTo(o)
+ }
+
+ moveFocusOffsetTo(fo) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveFocusOffsetTo` method is deprecated, please use `Range.moveFocusTo(offset)` instead.'
+ )
+
+ return this.moveFocusTo(fo)
+ }
+
+ moveStartOffsetTo(o) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveStartOffsetTo` method is deprecated, please use `Range.moveStartTo(offset)` instead.'
+ )
+
+ return this.moveStartTo(o)
+ }
+
+ moveEndOffsetTo(o) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveEndOffsetTo` method is deprecated, please use `Range.moveEndTo(offset)` instead.'
+ )
+
+ return this.moveEndTo(o)
+ }
+
+ moveOffsetsTo(ao, fo = ao) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveOffsetsTo` method is deprecated, please use `Range.moveAnchorTo` and `Range.moveFocusTo` in sequence instead.'
+ )
+
+ return this.moveAnchorTo(ao).moveFocusTo(fo)
+ }
+
+ moveAnchorToStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveAnchorToStartOf` method is deprecated, please use `Range.moveAnchorToStartOfNode` instead.'
+ )
+
+ return this.moveAnchorToStartOfNode(node)
+ }
+
+ moveAnchorToEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveAnchorToEndOf` method is deprecated, please use `Range.moveAnchorToEndOfNode` instead.'
+ )
+
+ return this.moveAnchorToEndOfNode(node)
+ }
+
+ moveFocusToStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveFocusToStartOf` method is deprecated, please use `Range.moveFocusToStartOfNode` instead.'
+ )
+
+ return this.moveFocusToStartOfNode(node)
+ }
+
+ moveFocusToEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveFocusToEndOf` method is deprecated, please use `Range.moveFocusToEndOfNode` instead.'
+ )
+
+ return this.moveFocusToEndOfNode(node)
+ }
+
+ moveToStartOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveToStartOf` method is deprecated, please use `Range.moveToStartOfNode` instead.'
+ )
+
+ return this.moveToStartOfNode(node)
+ }
+
+ moveToEndOf(node) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveToEndOf` method is deprecated, please use `Range.moveToEndOfNode` instead.'
+ )
+
+ return this.moveToEndOfNode(node)
+ }
+
+ moveToRangeOf(...args) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveToRangeOf` method is deprecated, please use `Range.moveToRangeOfNode` instead.'
+ )
+
+ return this.moveToRangeOfNode(...args)
+ }
+
+ collapseToAnchor() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.collapseToAnchor` method is deprecated, please use `Range.moveToAnchor` instead.'
+ )
+
+ return this.moveToAnchor()
+ }
+
+ collapseToEnd() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.collapseToEnd` method is deprecated, please use `Range.moveToEnd` instead.'
+ )
+
+ return this.moveToEnd()
+ }
+
+ collapseToFocus() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.collapseToFocus` method is deprecated, please use `Range.moveToFocus` instead.'
+ )
+
+ return this.moveToFocus()
+ }
+
+ collapseToStart() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.collapseToStart` method is deprecated, please use `Range.moveToStart` instead.'
+ )
+
+ return this.moveToStart()
+ }
+
+ move(n = 1) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.move` method is deprecated, please use `Range.moveForward` or `Range.moveBackward` instead.'
+ )
+
+ return n > 0 ? this.moveForward(n) : this.moveBackward(-n)
+ }
+
+ moveAnchor(n = 1) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveAnchor` method is deprecated, please use `Range.moveAnchorForward` or `Range.moveAnchorBackward` instead.'
+ )
+
+ return n > 0 ? this.moveAnchorForward(n) : this.moveAnchorBackward(-n)
+ }
+
+ moveEnd(n = 1) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveEnd` method is deprecated, please use `Range.moveEndForward` or `Range.moveEndBackward` instead.'
+ )
+
+ return n > 0 ? this.moveEndForward(n) : this.moveEndBackward(-n)
+ }
+
+ moveFocus(n = 1) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveFocus` method is deprecated, please use `Range.moveFocusForward` or `Range.moveFocusBackward` instead.'
+ )
+
+ return n > 0 ? this.moveFocusForward(n) : this.moveFocusBackward(-n)
+ }
+
+ moveStart(n = 1) {
+ logger.deprecate(
+ '0.37.0',
+ 'The `Range.moveStart` method is deprecated, please use `Range.moveStartForward` or `Range.moveStartBackward` instead.'
+ )
+
+ return n > 0 ? this.moveStartForward(n) : this.moveStartBackward(-n)
+ }
}
/**
@@ -873,104 +1387,31 @@ class Range extends Record(DEFAULTS) {
Range.prototype[MODEL_TYPES.RANGE] = true
-/**
- * Mix in some "move" convenience methods.
- */
-
-const MOVE_METHODS = [
- ['move', ''],
- ['move', 'To'],
- ['move', 'ToStartOf'],
- ['move', 'ToEndOf'],
-]
-
-MOVE_METHODS.forEach(([p, s]) => {
- Range.prototype[`${p}${s}`] = function(...args) {
- return this[`${p}Anchor${s}`](...args)[`${p}Focus${s}`](...args)
- }
-})
-
-/**
- * Mix in the "start", "end" and "edge" convenience methods.
- */
-
-const EDGE_METHODS = [
- ['has', 'AtStartOf', true],
- ['has', 'AtEndOf', true],
- ['has', 'Between', true],
- ['has', 'In', true],
- ['collapseTo', ''],
- ['move', ''],
- ['moveTo', ''],
- ['move', 'To'],
- ['move', 'OffsetTo'],
-]
-
-EDGE_METHODS.forEach(([p, s, hasEdge]) => {
- const anchor = `${p}Anchor${s}`
- const focus = `${p}Focus${s}`
-
- Range.prototype[`${p}Start${s}`] = function(...args) {
- return this.isBackward ? this[focus](...args) : this[anchor](...args)
- }
-
- Range.prototype[`${p}End${s}`] = function(...args) {
- return this.isBackward ? this[anchor](...args) : this[focus](...args)
- }
-
- if (hasEdge) {
- Range.prototype[`${p}Edge${s}`] = function(...args) {
- return this[anchor](...args) || this[focus](...args)
- }
- }
-})
-
/**
* Mix in some aliases for convenience / parallelism with the browser APIs.
*/
const ALIAS_METHODS = [
['collapseTo', 'moveTo'],
- ['collapseToAnchor', 'moveToAnchor'],
- ['collapseToFocus', 'moveToFocus'],
- ['collapseToStart', 'moveToStart'],
- ['collapseToEnd', 'moveToEnd'],
- ['collapseToStartOf', 'moveToStartOf'],
- ['collapseToEndOf', 'moveToEndOf'],
+ ['collapseToStartOf', 'moveToStartOfNode'],
+ ['collapseToEndOf', 'moveToEndOfNode'],
['extend', 'moveFocus'],
['extendTo', 'moveFocusTo'],
- ['extendToStartOf', 'moveFocusToStartOf'],
- ['extendToEndOf', 'moveFocusToEndOf'],
+ ['extendToStartOf', 'moveFocusToStartOfNode'],
+ ['extendToEndOf', 'moveFocusToEndOfNode'],
]
ALIAS_METHODS.forEach(([alias, method]) => {
Range.prototype[alias] = function(...args) {
+ logger.deprecate(
+ '0.37.0',
+ `The \`Range.${alias}\` method is deprecated, please use \`Range.${method}\` instead.`
+ )
+
return this[method](...args)
}
})
-/**
- * Get the first text of a `node`.
- *
- * @param {Node} node
- * @return {Text}
- */
-
-function getFirstText(node) {
- return node.object == 'text' ? node : node.getFirstText()
-}
-
-/**
- * Get the last text of a `node`.
- *
- * @param {Node} node
- * @return {Text}
- */
-
-function getLastText(node) {
- return node.object == 'text' ? node : node.getLastText()
-}
-
/**
* Export.
*
diff --git a/packages/slate/src/models/text.js b/packages/slate/src/models/text.js
index f8ba0aff5..4164773ed 100644
--- a/packages/slate/src/models/text.js
+++ b/packages/slate/src/models/text.js
@@ -313,13 +313,13 @@ class Text extends Record(DEFAULTS) {
const { key } = this
decorations.forEach(range => {
- const { startKey, endKey, startOffset, endOffset, marks } = range
- const hasStart = startKey == key
- const hasEnd = endKey == key
+ const { start, end, marks } = range
+ const hasStart = start.key == key
+ const hasEnd = end.key == key
if (hasStart && hasEnd) {
- const index = hasStart ? startOffset : 0
- const length = hasEnd ? endOffset - index : this.text.length - index
+ const index = hasStart ? start.offset : 0
+ const length = hasEnd ? end.offset - index : this.text.length - index
if (length < 1) return
if (index >= this.text.length) return
diff --git a/packages/slate/src/models/value.js b/packages/slate/src/models/value.js
index 7fac793d4..eed1fe8f0 100644
--- a/packages/slate/src/models/value.js
+++ b/packages/slate/src/models/value.js
@@ -118,10 +118,12 @@ class Value extends Record(DEFAULTS) {
if (selection.isUnset) {
const text = document.getFirstText()
- if (text) selection = selection.collapseToStartOf(text)
+ if (text) selection = selection.moveToStartOfNode(text)
selection = document.createRange(selection)
}
+ selection = document.createRange(selection)
+
let value = new Value({
data,
document,
@@ -212,166 +214,6 @@ class Value extends Record(DEFAULTS) {
return this.selection.isFocused
}
- /**
- * Is the current selection collapsed?
- *
- * @return {Boolean}
- */
-
- get isCollapsed() {
- return this.selection.isCollapsed
- }
-
- /**
- * Is the current selection expanded?
- *
- * @return {Boolean}
- */
-
- get isExpanded() {
- return this.selection.isExpanded
- }
-
- /**
- * Is the current selection backward?
- *
- * @return {Boolean} isBackward
- */
-
- get isBackward() {
- return this.selection.isBackward
- }
-
- /**
- * Is the current selection forward?
- *
- * @return {Boolean}
- */
-
- get isForward() {
- return this.selection.isForward
- }
-
- /**
- * Get the current start key.
- *
- * @return {String}
- */
-
- get startKey() {
- return this.selection.startKey
- }
-
- /**
- * Get the current end key.
- *
- * @return {String}
- */
-
- get endKey() {
- return this.selection.endKey
- }
-
- /**
- * Get the current start path.
- *
- * @return {String}
- */
-
- get startPath() {
- return this.selection.startPath
- }
-
- /**
- * Get the current end path.
- *
- * @return {String}
- */
-
- get endPath() {
- return this.selection.endPath
- }
-
- /**
- * Get the current start offset.
- *
- * @return {String}
- */
-
- get startOffset() {
- return this.selection.startOffset
- }
-
- /**
- * Get the current end offset.
- *
- * @return {String}
- */
-
- get endOffset() {
- return this.selection.endOffset
- }
-
- /**
- * Get the current anchor key.
- *
- * @return {String}
- */
-
- get anchorKey() {
- return this.selection.anchorKey
- }
-
- /**
- * Get the current focus key.
- *
- * @return {String}
- */
-
- get focusKey() {
- return this.selection.focusKey
- }
-
- /**
- * Get the current anchor path.
- *
- * @return {String}
- */
-
- get anchorPath() {
- return this.selection.anchorPath
- }
-
- /**
- * Get the current focus path.
- *
- * @return {String}
- */
-
- get focusPath() {
- return this.selection.focusPath
- }
-
- /**
- * Get the current anchor offset.
- *
- * @return {String}
- */
-
- get anchorOffset() {
- return this.selection.anchorOffset
- }
-
- /**
- * Get the current focus offset.
- *
- * @return {String}
- */
-
- get focusOffset() {
- return this.selection.focusOffset
- }
-
/**
* Get the current start text node's closest block parent.
*
@@ -379,7 +221,10 @@ class Value extends Record(DEFAULTS) {
*/
get startBlock() {
- return this.startKey && this.document.getClosestBlock(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getClosestBlock(this.selection.start.key)
+ )
}
/**
@@ -389,7 +234,10 @@ class Value extends Record(DEFAULTS) {
*/
get endBlock() {
- return this.endKey && this.document.getClosestBlock(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getClosestBlock(this.selection.end.key)
+ )
}
/**
@@ -399,7 +247,10 @@ class Value extends Record(DEFAULTS) {
*/
get anchorBlock() {
- return this.anchorKey && this.document.getClosestBlock(this.anchorKey)
+ return (
+ this.selection.anchor.key &&
+ this.document.getClosestBlock(this.selection.anchor.key)
+ )
}
/**
@@ -409,7 +260,10 @@ class Value extends Record(DEFAULTS) {
*/
get focusBlock() {
- return this.focusKey && this.document.getClosestBlock(this.focusKey)
+ return (
+ this.selection.focus.key &&
+ this.document.getClosestBlock(this.selection.focus.key)
+ )
}
/**
@@ -419,7 +273,10 @@ class Value extends Record(DEFAULTS) {
*/
get startInline() {
- return this.startKey && this.document.getClosestInline(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getClosestInline(this.selection.start.key)
+ )
}
/**
@@ -429,7 +286,10 @@ class Value extends Record(DEFAULTS) {
*/
get endInline() {
- return this.endKey && this.document.getClosestInline(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getClosestInline(this.selection.end.key)
+ )
}
/**
@@ -439,7 +299,10 @@ class Value extends Record(DEFAULTS) {
*/
get anchorInline() {
- return this.anchorKey && this.document.getClosestInline(this.anchorKey)
+ return (
+ this.selection.anchor.key &&
+ this.document.getClosestInline(this.selection.anchor.key)
+ )
}
/**
@@ -449,7 +312,10 @@ class Value extends Record(DEFAULTS) {
*/
get focusInline() {
- return this.focusKey && this.document.getClosestInline(this.focusKey)
+ return (
+ this.selection.focus.key &&
+ this.document.getClosestInline(this.selection.focus.key)
+ )
}
/**
@@ -459,7 +325,10 @@ class Value extends Record(DEFAULTS) {
*/
get startText() {
- return this.startKey && this.document.getDescendant(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getDescendant(this.selection.start.key)
+ )
}
/**
@@ -469,7 +338,10 @@ class Value extends Record(DEFAULTS) {
*/
get endText() {
- return this.endKey && this.document.getDescendant(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getDescendant(this.selection.end.key)
+ )
}
/**
@@ -479,7 +351,10 @@ class Value extends Record(DEFAULTS) {
*/
get anchorText() {
- return this.anchorKey && this.document.getDescendant(this.anchorKey)
+ return (
+ this.selection.anchor.key &&
+ this.document.getDescendant(this.selection.anchor.key)
+ )
}
/**
@@ -489,7 +364,10 @@ class Value extends Record(DEFAULTS) {
*/
get focusText() {
- return this.focusKey && this.document.getDescendant(this.focusKey)
+ return (
+ this.selection.focus.key &&
+ this.document.getDescendant(this.selection.focus.key)
+ )
}
/**
@@ -499,7 +377,10 @@ class Value extends Record(DEFAULTS) {
*/
get nextBlock() {
- return this.endKey && this.document.getNextBlock(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getNextBlock(this.selection.end.key)
+ )
}
/**
@@ -509,7 +390,10 @@ class Value extends Record(DEFAULTS) {
*/
get previousBlock() {
- return this.startKey && this.document.getPreviousBlock(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getPreviousBlock(this.selection.start.key)
+ )
}
/**
@@ -519,7 +403,10 @@ class Value extends Record(DEFAULTS) {
*/
get nextInline() {
- return this.endKey && this.document.getNextInline(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getNextInline(this.selection.end.key)
+ )
}
/**
@@ -529,7 +416,10 @@ class Value extends Record(DEFAULTS) {
*/
get previousInline() {
- return this.startKey && this.document.getPreviousInline(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getPreviousInline(this.selection.start.key)
+ )
}
/**
@@ -539,7 +429,10 @@ class Value extends Record(DEFAULTS) {
*/
get nextText() {
- return this.endKey && this.document.getNextText(this.endKey)
+ return (
+ this.selection.end.key &&
+ this.document.getNextText(this.selection.end.key)
+ )
}
/**
@@ -549,7 +442,10 @@ class Value extends Record(DEFAULTS) {
*/
get previousText() {
- return this.startKey && this.document.getPreviousText(this.startKey)
+ return (
+ this.selection.start.key &&
+ this.document.getPreviousText(this.selection.start.key)
+ )
}
/**
@@ -644,8 +540,9 @@ class Value extends Record(DEFAULTS) {
*/
get isEmpty() {
- if (this.isCollapsed) return true
- if (this.endOffset != 0 && this.startOffset != 0) return false
+ if (this.selection.isCollapsed) return true
+ if (this.selection.end.offset != 0 && this.selection.start.offset != 0)
+ return false
return this.fragment.isEmpty
}
@@ -656,8 +553,8 @@ class Value extends Record(DEFAULTS) {
*/
get isInVoid() {
- if (this.isExpanded) return false
- return this.document.hasVoidParent(this.startKey)
+ if (this.selection.isExpanded) return false
+ return this.document.hasVoidParent(this.selection.start.key)
}
/**
@@ -704,7 +601,10 @@ class Value extends Record(DEFAULTS) {
value = value.set('document', document)
value = value.mapRanges(range => {
- return range.merge({ anchorPath: null, focusPath: null })
+ return range.setPoints([
+ range.anchor.setPath(null),
+ range.focus.setPath(null),
+ ])
})
return value
@@ -728,36 +628,30 @@ class Value extends Record(DEFAULTS) {
// Update any ranges that were affected.
const node = document.assertNode(path)
+
+ value = value.mapRanges(range => {
+ const { anchor, focus, isBackward, isAtomic } = range
+
+ if (
+ anchor.key === node.key &&
+ (anchor.offset > offset ||
+ (anchor.offset === offset && (!isAtomic || !isBackward)))
+ ) {
+ range = range.moveAnchorForward(text.length)
+ }
+
+ if (
+ focus.key === node.key &&
+ (focus.offset > offset ||
+ (focus.offset == offset && (!isAtomic || isBackward)))
+ ) {
+ range = range.moveFocusForward(text.length)
+ }
+
+ return range
+ })
+
value = value.clearAtomicRanges(node.key, offset)
-
- value = value.mapRanges(range => {
- const { anchorKey, anchorOffset, isBackward, isAtomic } = range
-
- if (
- anchorKey === node.key &&
- (anchorOffset > offset ||
- (anchorOffset === offset && (!isAtomic || !isBackward)))
- ) {
- return range.moveAnchor(text.length)
- }
-
- return range
- })
-
- value = value.mapRanges(range => {
- const { focusKey, focusOffset, isBackward, isAtomic } = range
-
- if (
- focusKey === node.key &&
- (focusOffset > offset ||
- (focusOffset == offset && (!isAtomic || isBackward)))
- ) {
- return range.moveFocus(text.length)
- }
-
- return range
- })
-
return value
}
@@ -782,16 +676,20 @@ class Value extends Record(DEFAULTS) {
if (two.object === 'text') {
const max = one.text.length
- if (range.anchorKey === two.key) {
- range = range.moveAnchorTo(one.key, max + range.anchorOffset)
+ if (range.anchor.key === two.key) {
+ range = range.moveAnchorTo(one.key, max + range.anchor.offset)
}
- if (range.focusKey === two.key) {
- range = range.moveFocusTo(one.key, max + range.focusOffset)
+ if (range.focus.key === two.key) {
+ range = range.moveFocusTo(one.key, max + range.focus.offset)
}
}
- range = range.merge({ anchorPath: null, focusPath: null })
+ range = range.setPoints([
+ range.anchor.setPath(null),
+ range.focus.setPath(null),
+ ])
+
return range
})
@@ -817,7 +715,10 @@ class Value extends Record(DEFAULTS) {
value = value.set('document', document)
value = value.mapRanges(range => {
- return range.merge({ anchorPath: null, focusPath: null })
+ return range.setPoints([
+ range.anchor.setPath(null),
+ range.focus.setPath(null),
+ ])
})
return value
@@ -861,21 +762,25 @@ class Value extends Record(DEFAULTS) {
value = value.set('document', document)
value = value.mapRanges(range => {
- const { startKey, endKey } = range
+ const { start, end } = range
- if (node.hasNode(startKey)) {
+ if (node.hasNode(start.key)) {
range = prev
? range.moveStartTo(prev.key, prev.text.length)
- : next ? range.moveStartTo(next.key, 0) : range.deselect()
+ : next ? range.moveStartTo(next.key, 0) : Range.create()
}
- if (node.hasNode(endKey)) {
+ if (node.hasNode(end.key)) {
range = prev
? range.moveEndTo(prev.key, prev.text.length)
- : next ? range.moveEndTo(next.key, 0) : range.deselect()
+ : next ? range.moveEndTo(next.key, 0) : Range.create()
}
- range = range.merge({ anchorPath: null, focusPath: null })
+ range = range.setPoints([
+ range.anchor.setPath(null),
+ range.focus.setPath(null),
+ ])
+
return range
})
@@ -903,28 +808,24 @@ class Value extends Record(DEFAULTS) {
value = value.clearAtomicRanges(node.key, offset, offset + length)
value = value.mapRanges(range => {
- const { anchorKey } = range
+ const { anchor, focus } = range
- if (anchorKey === node.key) {
- return range.anchorOffset >= rangeOffset
- ? range.moveAnchor(-length)
- : range.anchorOffset > offset
- ? range.moveAnchorTo(range.anchorKey, offset)
- : range
+ if (anchor.key === node.key) {
+ range =
+ anchor.offset >= rangeOffset
+ ? range.moveAnchorBackward(length)
+ : anchor.offset > offset
+ ? range.moveAnchorTo(anchor.key, offset)
+ : range
}
- return range
- })
-
- value = value.mapRanges(range => {
- const { focusKey } = range
-
- if (focusKey === node.key) {
- return range.focusOffset >= rangeOffset
- ? range.moveFocus(-length)
- : range.focusOffset > offset
- ? range.moveFocusTo(range.focusKey, offset)
- : range
+ if (focus.key === node.key) {
+ range =
+ focus.offset >= rangeOffset
+ ? range.moveFocusBackward(length)
+ : focus.offset > offset
+ ? range.moveFocusTo(focus.key, offset)
+ : range
}
return range
@@ -979,8 +880,8 @@ class Value extends Record(DEFAULTS) {
setSelection(properties) {
let value = this
let { document, selection } = value
- const next = selection.merge(properties)
- selection = document.createRange(next)
+ const next = selection.setProperties(properties)
+ selection = document.resolveRange(next)
value = value.set('selection', selection)
return value
}
@@ -1004,19 +905,23 @@ class Value extends Record(DEFAULTS) {
value = value.mapRanges(range => {
const next = newDocument.getNextText(node.key)
- const { startKey, startOffset, endKey, endOffset } = range
+ const { start, end } = range
// If the start was after the split, move it to the next node.
- if (node.key === startKey && position <= startOffset) {
- range = range.moveStartTo(next.key, startOffset - position)
+ if (node.key === start.key && position <= start.offset) {
+ range = range.moveStartTo(next.key, start.offset - position)
}
// If the end was after the split, move it to the next node.
- if (node.key === endKey && position <= endOffset) {
- range = range.moveEndTo(next.key, endOffset - position)
+ if (node.key === end.key && position <= end.offset) {
+ range = range.moveEndTo(next.key, end.offset - position)
}
- range = range.merge({ anchorPath: null, focusPath: null })
+ range = range.setPoints([
+ range.anchor.setPath(null),
+ range.focus.setPath(null),
+ ])
+
return range
})
@@ -1036,7 +941,7 @@ class Value extends Record(DEFAULTS) {
if (selection) {
let next = selection.isSet ? iterator(selection) : selection
- if (!next) next = selection.deselect()
+ if (!next) next = Range.create()
if (next !== selection) next = document.createRange(next)
value = value.set('selection', next)
}
@@ -1060,25 +965,25 @@ class Value extends Record(DEFAULTS) {
* Remove any atomic ranges inside a `key`, `offset` and `length`.
*
* @param {String} key
- * @param {Number} start
- * @param {Number?} end
+ * @param {Number} from
+ * @param {Number?} to
* @return {Value}
*/
- clearAtomicRanges(key, start, end = null) {
+ clearAtomicRanges(key, from, to = null) {
return this.mapRanges(range => {
- const { isAtomic, startKey, startOffset, endKey, endOffset } = range
+ const { isAtomic, start, end } = range
if (!isAtomic) return range
- if (startKey !== key) return range
+ if (start.key !== key) return range
- if (startOffset < start && (endKey !== key || endOffset > start)) {
+ if (start.offset < from && (end.key !== key || end.offset > from)) {
return null
}
if (
- end != null &&
- startOffset < end &&
- (endKey !== key || endOffset > end)
+ to != null &&
+ start.offset < to &&
+ (end.key !== key || end.offset > to)
) {
return null
}
@@ -1132,6 +1037,154 @@ class Value extends Record(DEFAULTS) {
toJS(options) {
return this.toJSON(options)
}
+
+ /**
+ * Deprecated.
+ */
+
+ get isCollapsed() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.isCollapsed` property is deprecated, please use `selection.isCollapsed` instead.'
+ )
+
+ return this.selection.isCollapsed
+ }
+
+ get isExpanded() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.isExpanded` property is deprecated, please use `selection.isExpanded` instead.'
+ )
+
+ return this.selection.isExpanded
+ }
+
+ get isBackward() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.isBackward` property is deprecated, please use `selection.isBackward` instead.'
+ )
+
+ return this.selection.isBackward
+ }
+
+ get isForward() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.isForward` property is deprecated, please use `selection.isForward` instead.'
+ )
+
+ return this.selection.isForward
+ }
+
+ get startKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.startKey` property is deprecated, please use `selection.start.key` instead.'
+ )
+
+ return this.selection.start.key
+ }
+
+ get endKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.endKey` property is deprecated, please use `selection.end.key` instead.'
+ )
+
+ return this.selection.end.key
+ }
+
+ get startPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.startPath` property is deprecated, please use `selection.start.path` instead.'
+ )
+
+ return this.selection.start.path
+ }
+
+ get endPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.endPath` property is deprecated, please use `selection.end.path` instead.'
+ )
+
+ return this.selection.end.path
+ }
+
+ get startOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.startOffset` property is deprecated, please use `selection.start.offset` instead.'
+ )
+
+ return this.selection.start.offset
+ }
+
+ get endOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.endOffset` property is deprecated, please use `selection.end.offset` instead.'
+ )
+
+ return this.selection.end.offset
+ }
+
+ get anchorKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.anchorKey` property is deprecated, please use `selection.anchor.key` instead.'
+ )
+
+ return this.selection.anchor.key
+ }
+
+ get focusKey() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.focusKey` property is deprecated, please use `selection.focus.key` instead.'
+ )
+
+ return this.selection.focus.key
+ }
+
+ get anchorPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.anchorPath` property is deprecated, please use `selection.anchor.path` instead.'
+ )
+
+ return this.selection.anchor.path
+ }
+
+ get focusPath() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.focusPath` property is deprecated, please use `selection.focus.path` instead.'
+ )
+
+ return this.selection.focus.path
+ }
+
+ get anchorOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.anchorOffset` property is deprecated, please use `selection.anchor.offset` instead.'
+ )
+
+ return this.selection.anchor.offset
+ }
+
+ get focusOffset() {
+ logger.deprecate(
+ '0.37.0',
+ 'The `value.focusOffset` property is deprecated, please use `selection.focus.offset` instead.'
+ )
+
+ return this.selection.focus.offset
+ }
}
/**
diff --git a/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js b/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js
index 7c677c776..7fe430cd0 100644
--- a/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js
+++ b/packages/slate/test/changes/at-current-range/insert-fragment/hanging-selection-single-block.js
@@ -28,9 +28,6 @@ export const input = (
)
-// The cursor position of insertFragment has some problems;
-// If you submit PR to fixed cursor position restore in insertFragment;
-// Please change this test as well
export const output = (
diff --git a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks-with-data.js b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks-with-data.js
index 9b6673121..9234de03f 100644
--- a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks-with-data.js
+++ b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks-with-data.js
@@ -3,9 +3,9 @@
import h from '../../../helpers/h'
export default function(change) {
- const { anchorKey, anchorOffset } = change.value
+ const { anchor } = change.value.selection
- change.replaceTextByKey(anchorKey, anchorOffset, 3, 'cat is cute', [
+ change.replaceTextByKey(anchor.key, anchor.offset, 3, 'cat is cute', [
{ type: 'font-size', data: { size: 16 } },
])
}
diff --git a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks.js b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks.js
index f8c2941a2..b7007322c 100644
--- a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks.js
+++ b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-active-marks.js
@@ -3,8 +3,8 @@
import h from '../../../helpers/h'
export default function(change) {
- const { anchorKey, anchorOffset } = change.value
- change.replaceTextByKey(anchorKey, anchorOffset, 3, 'cat is cute')
+ const { anchor } = change.value.selection
+ change.replaceTextByKey(anchor.key, anchor.offset, 3, 'cat is cute')
}
export const input = (
diff --git a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-mark-and-active-mark.js b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-mark-and-active-mark.js
index c1ce898cf..2159f4ea8 100644
--- a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-mark-and-active-mark.js
+++ b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-mark-and-active-mark.js
@@ -3,9 +3,9 @@
import h from '../../../helpers/h'
export default function(change) {
- const { anchorKey, anchorOffset } = change.value
+ const { anchor } = change.value.selection
- change.replaceTextByKey(anchorKey, anchorOffset, 3, 'cat is cute', [
+ change.replaceTextByKey(anchor.key, anchor.offset, 3, 'cat is cute', [
{ type: 'italic' },
])
}
diff --git a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-node-index-mark.js b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-node-index-mark.js
index a91b3a92a..f12c80607 100644
--- a/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-node-index-mark.js
+++ b/packages/slate/test/changes/by-key/replace-text-by-key/replace-with-node-index-mark.js
@@ -3,8 +3,8 @@
import h from '../../../helpers/h'
export default function(change) {
- const { anchorKey, anchorOffset } = change.value
- change.replaceTextByKey(anchorKey, anchorOffset, 3, 'cat is cute')
+ const { anchor } = change.value.selection
+ change.replaceTextByKey(anchor.key, anchor.offset, 3, 'cat is cute')
}
export const input = (
diff --git a/packages/slate/test/changes/by-key/replace-text-by-key/replace-without-any-marks.js b/packages/slate/test/changes/by-key/replace-text-by-key/replace-without-any-marks.js
index 63dbe3b16..a489ca787 100644
--- a/packages/slate/test/changes/by-key/replace-text-by-key/replace-without-any-marks.js
+++ b/packages/slate/test/changes/by-key/replace-text-by-key/replace-without-any-marks.js
@@ -3,8 +3,8 @@
import h from '../../../helpers/h'
export default function(change) {
- const { anchorKey, anchorOffset } = change.value
- change.replaceTextByKey(anchorKey, anchorOffset, 3, 'cat is cute')
+ const { anchor } = change.value.selection
+ change.replaceTextByKey(anchor.key, anchor.offset, 3, 'cat is cute')
}
export const input = (
diff --git a/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string-and-mark.js b/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string-and-mark.js
index 271275003..ba9829154 100644
--- a/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string-and-mark.js
+++ b/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string-and-mark.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.setTextByKey(change.value.anchorKey, 'cat is cute', [
+ change.setTextByKey(change.value.selection.anchor.key, 'cat is cute', [
{ type: 'bold' },
{ type: 'italic' },
])
diff --git a/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string.js b/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string.js
index 153305ea7..7ecfd3ea4 100644
--- a/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string.js
+++ b/packages/slate/test/changes/by-key/set-text-by-key/replace-with-string.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.setTextByKey(change.value.anchorKey, 'cat is cute')
+ change.setTextByKey(change.value.selection.anchor.key, 'cat is cute')
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-end/already-collapsed.js b/packages/slate/test/changes/on-selection/move-to-anchor/already-collapsed.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-end/already-collapsed.js
rename to packages/slate/test/changes/on-selection/move-to-anchor/already-collapsed.js
index fb1a38b48..4fb1ffb32 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-end/already-collapsed.js
+++ b/packages/slate/test/changes/on-selection/move-to-anchor/already-collapsed.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToEnd()
+ change.moveToAnchor()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-start/basic.js b/packages/slate/test/changes/on-selection/move-to-anchor/basic.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-start/basic.js
rename to packages/slate/test/changes/on-selection/move-to-anchor/basic.js
index b92027eb9..c0095c12b 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-start/basic.js
+++ b/packages/slate/test/changes/on-selection/move-to-anchor/basic.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToStart()
+ change.moveToAnchor()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-start/already-collapsed.js b/packages/slate/test/changes/on-selection/move-to-end/already-collapsed.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-start/already-collapsed.js
rename to packages/slate/test/changes/on-selection/move-to-end/already-collapsed.js
index 69703022a..3a9da49a1 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-start/already-collapsed.js
+++ b/packages/slate/test/changes/on-selection/move-to-end/already-collapsed.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToStart()
+ change.moveToEnd()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-end/basic.js b/packages/slate/test/changes/on-selection/move-to-end/basic.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-end/basic.js
rename to packages/slate/test/changes/on-selection/move-to-end/basic.js
index 81e88a288..d097bb376 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-end/basic.js
+++ b/packages/slate/test/changes/on-selection/move-to-end/basic.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToEnd()
+ change.moveToEnd()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-end/void.js b/packages/slate/test/changes/on-selection/move-to-end/void.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-end/void.js
rename to packages/slate/test/changes/on-selection/move-to-end/void.js
index b1842c0cd..d9d71e037 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-end/void.js
+++ b/packages/slate/test/changes/on-selection/move-to-end/void.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToEnd()
+ change.moveToEnd()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-focus/already-collapsed.js b/packages/slate/test/changes/on-selection/move-to-focus/already-collapsed.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-focus/already-collapsed.js
rename to packages/slate/test/changes/on-selection/move-to-focus/already-collapsed.js
index 4d1bc40d3..07c938880 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-focus/already-collapsed.js
+++ b/packages/slate/test/changes/on-selection/move-to-focus/already-collapsed.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToFocus()
+ change.moveToFocus()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-focus/basic.js b/packages/slate/test/changes/on-selection/move-to-focus/basic.js
similarity index 93%
rename from packages/slate/test/changes/on-selection/collapse-to-focus/basic.js
rename to packages/slate/test/changes/on-selection/move-to-focus/basic.js
index 382c6fac1..fcf66407a 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-focus/basic.js
+++ b/packages/slate/test/changes/on-selection/move-to-focus/basic.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToFocus()
+ change.moveToFocus()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/select-all/basic.js b/packages/slate/test/changes/on-selection/move-to-range-of-document/basic.js
similarity index 94%
rename from packages/slate/test/changes/on-selection/select-all/basic.js
rename to packages/slate/test/changes/on-selection/move-to-range-of-document/basic.js
index 9b89fa2aa..902a895b2 100644
--- a/packages/slate/test/changes/on-selection/select-all/basic.js
+++ b/packages/slate/test/changes/on-selection/move-to-range-of-document/basic.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.selectAll()
+ change.moveToRangeOfDocument()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-anchor/already-collapsed.js b/packages/slate/test/changes/on-selection/move-to-start/already-collapsed.js
similarity index 92%
rename from packages/slate/test/changes/on-selection/collapse-to-anchor/already-collapsed.js
rename to packages/slate/test/changes/on-selection/move-to-start/already-collapsed.js
index 067549e3f..d36a55573 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-anchor/already-collapsed.js
+++ b/packages/slate/test/changes/on-selection/move-to-start/already-collapsed.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToAnchor()
+ change.moveToStart()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-anchor/basic.js b/packages/slate/test/changes/on-selection/move-to-start/basic.js
similarity index 92%
rename from packages/slate/test/changes/on-selection/collapse-to-anchor/basic.js
rename to packages/slate/test/changes/on-selection/move-to-start/basic.js
index f07f958c7..3bc38680b 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-anchor/basic.js
+++ b/packages/slate/test/changes/on-selection/move-to-start/basic.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToAnchor()
+ change.moveToStart()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/collapse-to-start/void.js b/packages/slate/test/changes/on-selection/move-to-start/void.js
similarity index 92%
rename from packages/slate/test/changes/on-selection/collapse-to-start/void.js
rename to packages/slate/test/changes/on-selection/move-to-start/void.js
index fc2720b00..70fc2cd2e 100644
--- a/packages/slate/test/changes/on-selection/collapse-to-start/void.js
+++ b/packages/slate/test/changes/on-selection/move-to-start/void.js
@@ -3,7 +3,7 @@
import h from '../../../helpers/h'
export default function(change) {
- change.collapseToStart()
+ change.moveToStart()
}
export const input = (
diff --git a/packages/slate/test/changes/on-selection/move-to/with-object.js b/packages/slate/test/changes/on-selection/select/with-object.js
similarity index 75%
rename from packages/slate/test/changes/on-selection/move-to/with-object.js
rename to packages/slate/test/changes/on-selection/select/with-object.js
index 00121b400..e28572910 100644
--- a/packages/slate/test/changes/on-selection/move-to/with-object.js
+++ b/packages/slate/test/changes/on-selection/select/with-object.js
@@ -7,10 +7,14 @@ export default function(change) {
const { startText } = value
change.select({
- anchorKey: startText.key,
- anchorOffset: 0,
- focusKey: startText.key,
- focusOffset: startText.text.length,
+ anchor: {
+ key: startText.key,
+ offset: 0,
+ },
+ focus: {
+ key: startText.key,
+ offset: startText.text.length,
+ },
})
}
diff --git a/packages/slate/test/changes/on-selection/move-to/with-selection.js b/packages/slate/test/changes/on-selection/select/with-selection.js
similarity index 71%
rename from packages/slate/test/changes/on-selection/move-to/with-selection.js
rename to packages/slate/test/changes/on-selection/select/with-selection.js
index bd3d0e515..f3a52bf19 100644
--- a/packages/slate/test/changes/on-selection/move-to/with-selection.js
+++ b/packages/slate/test/changes/on-selection/select/with-selection.js
@@ -5,11 +5,15 @@ import h from '../../../helpers/h'
export default function(change) {
const { value } = change
const { selection, startText } = value
- const range = selection.merge({
- anchorKey: startText.key,
- anchorOffset: 0,
- focusKey: startText.key,
- focusOffset: startText.text.length,
+ const range = selection.setProperties({
+ anchor: {
+ key: startText.key,
+ offset: 0,
+ },
+ focus: {
+ key: startText.key,
+ offset: startText.text.length,
+ },
})
change.select(range)
diff --git a/packages/slate/test/history/undo/insert-text-not-contiguous.js b/packages/slate/test/history/undo/insert-text-not-contiguous.js
index ca7e2ddbb..0cc240349 100644
--- a/packages/slate/test/history/undo/insert-text-not-contiguous.js
+++ b/packages/slate/test/history/undo/insert-text-not-contiguous.js
@@ -3,17 +3,17 @@
import h from '../../helpers/h'
export default function(value) {
- return value
+ const a = value.change().insertText('t').value
+ const b = a
.change()
- .insertText('t')
- .value.change()
- .move(-1)
- .insertText('w')
- .value.change()
- .move(-1)
- .insertText('o')
- .value.change()
- .undo().value
+ .moveBackward(1)
+ .insertText('w').value
+ const c = b
+ .change()
+ .moveBackward(1)
+ .insertText('o').value
+ const d = c.change().undo().value
+ return d
}
export const input = (
diff --git a/packages/slate/test/serializers/raw/serialize/preserve-selection-and-keys.js b/packages/slate/test/serializers/raw/serialize/preserve-selection-and-keys.js
index 9f81b480b..499123101 100644
--- a/packages/slate/test/serializers/raw/serialize/preserve-selection-and-keys.js
+++ b/packages/slate/test/serializers/raw/serialize/preserve-selection-and-keys.js
@@ -43,12 +43,18 @@ export const output = {
},
selection: {
object: 'range',
- anchorKey: '0',
- anchorPath: [0, 0],
- anchorOffset: 0,
- focusKey: '0',
- focusPath: [0, 0],
- focusOffset: 0,
+ anchor: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ key: '0',
+ path: [0, 0],
+ offset: 0,
+ },
isFocused: false,
marks: null,
isAtomic: false,
diff --git a/packages/slate/test/serializers/raw/serialize/preserve-selection.js b/packages/slate/test/serializers/raw/serialize/preserve-selection.js
index 8f20c7b9b..d177cc51a 100644
--- a/packages/slate/test/serializers/raw/serialize/preserve-selection.js
+++ b/packages/slate/test/serializers/raw/serialize/preserve-selection.js
@@ -38,10 +38,16 @@ export const output = {
},
selection: {
object: 'range',
- anchorPath: [0, 0],
- anchorOffset: 0,
- focusPath: [0, 0],
- focusOffset: 0,
+ anchor: {
+ object: 'point',
+ path: [0, 0],
+ offset: 0,
+ },
+ focus: {
+ object: 'point',
+ path: [0, 0],
+ offset: 0,
+ },
isFocused: false,
marks: null,
isAtomic: false,