diff --git a/lib/models/node.js b/lib/models/node.js index 179fcbd17..9d3206742 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -429,10 +429,11 @@ const Node = { * @return {Node || Void} */ - getDescedantAtPath(path) { + getDescendantAtPath(path) { let descendant = this for (const index of path) { + if (!descendant) return if (!descendant.nodes) return descendant = descendant.nodes.get(index) } @@ -770,7 +771,7 @@ const Node = { while (parent = this.getParent(child)) { const index = parent.nodes.indexOf(child) - path.push(index) + path.unshift(index) child = parent } diff --git a/lib/transforms/by-key.js b/lib/transforms/by-key.js index 54cfae274..592e2caad 100644 --- a/lib/transforms/by-key.js +++ b/lib/transforms/by-key.js @@ -1,6 +1,205 @@ import Normalize from '../utils/normalize' +/** + * Insert `text` at `index` in node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {String} text + * @param {Set} marks (optional) + * @return {Transform} + */ + +export function insertTextByKey(transform, key, index, text, marks) { + let { state } = transform + let { document } = state + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.insertText(index, text, marks) + document = document.updateDescendant(node) + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'insert-text', + index, + marks, + path, + text, + }) + + return transform +} + +/** + * Remove text at `index` and `length` in node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {Number} length + * @return {Transform} + */ + +export function removeTextByKey(transform, key, index, length) { + let { state } = transform + let { document } = state + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.removeText(index, length) + document = document.updateDescendant(node) + document = document.normalize() + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'remove-text', + index, + length, + path, + }) + + return transform +} + +/** + * Add mark to text at `index` and `length` in node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {Number} length + * @param {Mark} mark + * @return {Transform} + */ + +export function addMarkByKey(transform, key, index, length, mark) { + let { state } = transform + let { document } = state + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.addMark(index, length, mark) + document = document.updateDescendant(node) + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'add-mark', + index, + length, + mark, + path, + }) + + return transform +} + +/** + * Remove mark from text at `index` and `length` in node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {Number} length + * @param {Mark} mark + * @return {Transform} + */ + +export function removeMarkByKey(transform, key, index, length, mark) { + let { state } = transform + let { document } = state + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.removeMark(index, length, mark) + document = document.updateDescendant(node) + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'remove-mark', + index, + length, + mark, + path, + }) + + return transform +} + +/** + * Set `properties` on mark on text at `index` and `length` in node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {Number} length + * @param {Mark} mark + * @return {Transform} + */ + +export function setMarkByKey(transform, key, index, length, mark, properties) { + properties = Normalize.markProperties(properties) + let { state } = transform + let { document } = state + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.updateMark(index, length, mark, properties) + document = document.updateDescendant(node) + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'set-mark', + index, + length, + mark, + path, + properties, + }) + + return transform +} + +/** + * Insert a `node` at `index` in a node by `key`. + * + * @param {Transform} transform + * @param {String} key + * @param {Number} index + * @param {Node} node + * @return {Transform} + */ + +export function insertNodeAfterNodeByKey(transform, key, index, node) { + let { state } = transform + let { document } = state + let parent = document.assertDescendant(key) + const path = document.getPath(parent) + const nodes = parent.nodes.splice(index + 1, 0, node) + + parent = parent.merge({ nodes }) + document = document.updateDescendant(parent) + document = document.normalize() + state = state.merge({ document }) + + transform.state = state + transform.operations.push({ + type: 'insert-node', + index, + node, + path, + }) + + return transform +} + /** * Remove a node by `key`. * @@ -12,10 +211,23 @@ import Normalize from '../utils/normalize' export function removeNodeByKey(transform, key) { let { state } = transform let { document } = state - document = document.removeDescendant(key) + const node = document.assertDescendant(key) + const path = document.getPath(node) + let parent = document.getParent(node) + const index = parent.nodes.indexOf(node) + const isParent = document == parent + + parent = parent.removeNode(index) + document = isParent ? parent : document.updateDescendant(parent) document = document.normalize() state = state.merge({ document }) + transform.state = state + transform.operations.push({ + type: 'remove-node', + path, + }) + return transform } @@ -24,7 +236,7 @@ export function removeNodeByKey(transform, key) { * * @param {Transform} transform * @param {String} key - * @param {Object or String} properties + * @param {Object || String} properties * @return {Transform} */ @@ -32,56 +244,62 @@ export function setNodeByKey(transform, key, properties) { properties = Normalize.nodeProperties(properties) let { state } = transform let { document } = state - let descendant = document.assertDescendant(key) - descendant = descendant.merge(properties) - document = document.updateDescendant(descendant) + let node = document.assertDescendant(key) + const path = document.getPath(node) + + node = node.merge(properties) + document = document.updateDescendant(node) + document = document.normalize() state = state.merge({ document }) + transform.state = state + transform.operations.push({ + type: 'set-node', + path, + properties, + }) + return transform } /** - * Insert a `node` after a node by `key`. + * Move a node by `key` to a new parent by `key` and `index`. * * @param {Transform} transform * @param {String} key - * @param {Node} node + * @param {String} newKey + * @param {Number} index * @return {Transform} */ -export function insertNodeAfterNodeByKey(transform, key, node) { +export function moveNodeByKey(transform, key, newKey, newIndex) { let { state } = transform let { document } = state - let descendant = document.assertDescendant(key) - let parent = document.getParent(key) - let index = parent.nodes.indexOf(descendant) - let nodes = parent.nodes.splice(index + 1, 0, node) - parent = parent.merge({ nodes }) - document = document.updateDescendant(parent) - state = state.merge({ document }) - transform.state = state - return transform -} - -/** - * Insert a `node` before a node by `key`. - * - * @param {Transform} transform - * @param {String} key - * @param {Node} node - * @return {Transform} - */ - -export function insertNodeBeforeNodeByKey(transform, key, node) { - let { state } = transform - let { document } = state - let descendant = document.assertDescendant(key) - let parent = document.getParent(key) - let index = parent.nodes.indexOf(descendant) - let nodes = parent.nodes.splice(index, 0, node) - parent = parent.merge({ nodes }) - document = document.updateDescendant(parent) + const node = document.assertDescendant(key) + const path = document.getPath(node) + const newPath = document.getPath(newKey) + let parent = document.getParent(node) + const isParent = document == parent + const index = parent.nodes.indexOf(node) + + parent = parent.removeNode(index) + document = isParent ? parent : document.updateDescendant(parent) + + let target = document.assertDescendant(newKey) + const isTarget = document == target + + target = target.insertNode(newIndex, node) + document = isTarget ? target : document.updateDescendant(target) + document = document.normalize() state = state.merge({ document }) + transform.state = state + transform.operations.push({ + type: 'move-node', + path, + newPath, + newIndex, + }) + return transform }