1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-23 23:42:56 +02:00

Add unwrapNodeByKey (#509)

* Add separate splitNodeOperation in two different op

* Add transform unwrapNodeByKey

* Add tests for unwrapNodeByKey

* Add test for undo of unwrapNodeByKey

* Support normalize option

* Handle last/first sibling case

* Document new unwrapNodeByKey transform
This commit is contained in:
Nicolas Gaborit
2016-12-08 19:37:34 +01:00
committed by Ian Storm Taylor
parent 36670d0d41
commit ed0395999c
19 changed files with 322 additions and 3 deletions

View File

@@ -59,6 +59,7 @@ Transform methods can either operate on the [`Document`](./document.md), the [`S
- [`splitNodeByKey`](#splitnodebykey) - [`splitNodeByKey`](#splitnodebykey)
- [`unwrapInlineByKey`](#unwrapinlinebykey) - [`unwrapInlineByKey`](#unwrapinlinebykey)
- [`unwrapBlockByKey`](#unwrapblockbykey) - [`unwrapBlockByKey`](#unwrapblockbykey)
- [`unwrapNodeByKey`](#unwrapnodebykey)
- [`wrapBlockByKey`](#wrapblockbykey) - [`wrapBlockByKey`](#wrapblockbykey)
- [`wrapInlineByKey`](#wrapinlinebykey) - [`wrapInlineByKey`](#wrapinlinebykey)
- [Document Transforms](#document-transforms) - [Document Transforms](#document-transforms)
@@ -333,6 +334,11 @@ Unwrap all inner content of an [`Inline`](./inline.md) node that match `properti
Unwrap all inner content of a [`Block`](./block.md) node that match `properties`. For convenience, you can pass a `type` string or `properties` object. Unwrap all inner content of a [`Block`](./block.md) node that match `properties`. For convenience, you can pass a `type` string or `properties` object.
### `unwrapNodeByKey`
`unwrapNodeByKey(key: String) => Transform`
Unwrap a single node from its parent. If the node is surrounded with siblings, its parent will be split. If the node is the only child, the parent is removed, and simply replaced by the node itself. Cannot unwrap a root node.
### `wrapBlockByKey` ### `wrapBlockByKey`
`wrapBlockByKey(key: String, properties: Object) => Transform` <br/> `wrapBlockByKey(key: String, properties: Object) => Transform` <br/>
`wrapBlockByKey(key: String, type: String) => Transform` `wrapBlockByKey(key: String, type: String) => Transform`

View File

@@ -276,7 +276,7 @@ export function splitNodeByKey(transform, key, offset, options = {}) {
let { document } = state let { document } = state
const path = document.getPath(key) const path = document.getPath(key)
transform.splitNodeOperation(path, offset) transform.splitNodeAtOffsetOperation(path, offset)
if (normalize) { if (normalize) {
const parent = document.getParent(key) const parent = document.getParent(key)
@@ -324,6 +324,63 @@ export function unwrapBlockByKey(transform, key, properties, options) {
transform.unwrapBlockAtRange(range, properties, options) transform.unwrapBlockAtRange(range, properties, options)
} }
/**
* Unwrap a single node from its parent.
*
* If the node is surrounded with siblings, its parent will be
* split. If the node is the only child, the parent is removed, and
* simply replaced by the node itself. Cannot unwrap a root node.
*
* @param {Transform} transform
* @param {String} key
* @param {Object} options
* @property {Boolean} normalize
*/
export function unwrapNodeByKey(transform, key, options = {}) {
const { normalize = true } = options
const { state } = transform
const { document } = state
const parent = document.getParent(key)
const node = parent.getChild(key)
const index = parent.nodes.indexOf(node)
const isFirst = index === 0
const isLast = index === parent.nodes.size - 1
const parentParent = document.getParent(parent.key)
const parentIndex = parentParent.nodes.indexOf(parent)
if (parent.nodes.size === 1) {
// Remove the parent
transform.removeNodeByKey(parent.key, { normalize: false })
// and replace it by the node itself
transform.insertNodeByKey(parentParent.key, parentIndex, node, options)
}
else if (isFirst) {
// Just move the node before its parent
transform.moveNodeByKey(key, parentParent.key, parentIndex, options)
}
else if (isLast) {
// Just move the node after its parent
transform.moveNodeByKey(key, parentParent.key, parentIndex + 1, options)
}
else {
const parentPath = document.getPath(parent.key)
// Split the parent
transform.splitNodeOperation(parentPath, index)
// Extract the node in between the splitted parent
transform.moveNodeByKey(key, parentParent.key, parentIndex + 1, { normalize: false })
if (normalize) {
transform.normalizeNodeByKey(parentParent.key, SCHEMA)
}
}
}
/** /**
* Wrap a node in an inline with `properties`. * Wrap a node in an inline with `properties`.

View File

@@ -29,6 +29,7 @@ import {
setMarkOperation, setMarkOperation,
setNodeOperation, setNodeOperation,
setSelectionOperation, setSelectionOperation,
splitNodeAtOffsetOperation,
splitNodeOperation, splitNodeOperation,
} from './operations' } from './operations'
@@ -114,6 +115,7 @@ import {
splitNodeByKey, splitNodeByKey,
unwrapInlineByKey, unwrapInlineByKey,
unwrapBlockByKey, unwrapBlockByKey,
unwrapNodeByKey,
wrapBlockByKey, wrapBlockByKey,
wrapInlineByKey, wrapInlineByKey,
} from './by-key' } from './by-key'
@@ -211,6 +213,7 @@ export default {
setMarkOperation, setMarkOperation,
setNodeOperation, setNodeOperation,
setSelectionOperation, setSelectionOperation,
splitNodeAtOffsetOperation,
splitNodeOperation, splitNodeOperation,
/** /**
@@ -290,6 +293,7 @@ export default {
splitNodeByKey, splitNodeByKey,
unwrapInlineByKey, unwrapInlineByKey,
unwrapBlockByKey, unwrapBlockByKey,
unwrapNodeByKey,
wrapBlockByKey, wrapBlockByKey,
wrapInlineByKey, wrapInlineByKey,

View File

@@ -432,7 +432,7 @@ export function setSelectionOperation(transform, properties, options = {}) {
* @param {Number} offset * @param {Number} offset
*/ */
export function splitNodeOperation(transform, path, offset) { export function splitNodeAtOffsetOperation(transform, path, offset) {
const inversePath = path.slice() const inversePath = path.slice()
inversePath[path.length - 1] += 1 inversePath[path.length - 1] += 1
@@ -440,13 +440,45 @@ export function splitNodeOperation(transform, path, offset) {
type: 'join_node', type: 'join_node',
path: inversePath, path: inversePath,
withPath: path, withPath: path,
deep: true // we need to join nodes recursively // we will split down to the text nodes, so we must join nodes recursively
deep: true
}] }]
const operation = { const operation = {
type: 'split_node', type: 'split_node',
path, path,
offset, offset,
count: null,
inverse,
}
transform.applyOperation(operation)
}
/**
* Split a node by `path` after its 'count' child.
*
* @param {Transform} transform
* @param {Array} path
* @param {Number} count
*/
export function splitNodeOperation(transform, path, count) {
const inversePath = path.slice()
inversePath[path.length - 1] += 1
const inverse = [{
type: 'join_node',
path: inversePath,
withPath: path,
deep: false
}]
const operation = {
type: 'split_node',
path,
offset: null,
count,
inverse, inverse,
} }

View File

@@ -0,0 +1,7 @@
export default function (state) {
return state
.transform()
.unwrapNodeByKey('to-unwrap')
.apply()
}

View File

@@ -0,0 +1,16 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
key: 'to-unwrap'
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
nodes:
- kind: text
text: word2

View File

@@ -0,0 +1,15 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word2

View File

@@ -0,0 +1,7 @@
export default function (state) {
return state
.transform()
.unwrapNodeByKey('to-unwrap')
.apply()
}

View File

@@ -0,0 +1,16 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
key: 'to-unwrap'
nodes:
- kind: text
text: word2

View File

@@ -0,0 +1,15 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
nodes:
- kind: text
text: word2

View File

@@ -0,0 +1,7 @@
export default function (state) {
return state
.transform()
.unwrapNodeByKey('to-unwrap')
.apply()
}

View File

@@ -0,0 +1,19 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
key: 'to-unwrap'
type: paragraph
nodes:
- kind: text
text: word
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word

View File

@@ -0,0 +1,15 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word

View File

@@ -0,0 +1,7 @@
export default function (state) {
return state
.transform()
.unwrapNodeByKey('to-unwrap')
.apply()
}

View File

@@ -0,0 +1,21 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
key: 'to-unwrap'
nodes:
- kind: text
text: word2
- kind: block
type: paragraph
nodes:
- kind: text
text: word3

View File

@@ -0,0 +1,23 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
nodes:
- kind: text
text: word2
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word3

View File

@@ -0,0 +1,11 @@
export default function (state) {
return state
.transform()
.unwrapNodeByKey('to-unwrap')
.apply()
.transform()
.undo()
.apply()
}

View File

@@ -0,0 +1,21 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
key: 'to-unwrap'
nodes:
- kind: text
text: word2
- kind: block
type: paragraph
nodes:
- kind: text
text: word3

View File

@@ -0,0 +1,20 @@
nodes:
- kind: block
type: quote
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: word1
- kind: block
type: paragraph
nodes:
- kind: text
text: word2
- kind: block
type: paragraph
nodes:
- kind: text
text: word3