diff --git a/lib/models/state.js b/lib/models/state.js index 121abf3f1..752792bda 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -1,10 +1,11 @@ import Document from './document' +import Mark from './mark' import Selection from './selection' import Transform from './transform' import uid from '../utils/uid' -import { Record, Stack } from 'immutable' +import { Record, Set, Stack } from 'immutable' /** * History. @@ -20,6 +21,7 @@ const History = new Record({ */ const DEFAULTS = { + cursorMarks: new Set(), document: new Document(), selection: new Selection(), history: new History(), @@ -283,7 +285,10 @@ class State extends new Record(DEFAULTS) { */ get marks() { - return this.document.getMarksAtRange(this.selection) + const set = this.document.getMarksAtRange(this.selection) + return this.selection.isExpanded + ? set + : set.union(this.cursorMarks) } /** @@ -337,210 +342,6 @@ class State extends new Record(DEFAULTS) { return new Transform({ state }) } - /** - * Delete at the current selection. - * - * @return {State} state - */ - - delete() { - let state = this - let { document, selection } = state - - // When collapsed, there's nothing to do. - if (selection.isCollapsed) return state - - // Otherwise, delete and update the selection. - document = document.deleteAtRange(selection) - selection = selection.collapseToStart() - state = state.merge({ document, selection }) - return state - } - - /** - * Delete backward `n` characters at the current selection. - * - * @param {Number} n (optional) - * @return {State} state - */ - - deleteBackward(n = 1) { - let state = this - let { document, selection } = state - let after = selection - - // Determine what the selection should be after deleting. - const { startKey } = selection - const startNode = document.getDescendant(startKey) - - if (selection.isExpanded) { - after = selection.collapseToStart() - } - - else if (selection.isAtStartOf(document)) { - after = selection - } - - else if (selection.isAtStartOf(startNode)) { - const parent = document.getParent(startNode) - const previous = document.getPreviousSibling(parent).nodes.first() - after = selection.collapseToEndOf(previous) - } - - else { - after = selection.moveBackward(n) - } - - // Delete backward and then update the selection. - document = document.deleteBackwardAtRange(selection) - selection = after - state = state.merge({ document, selection }) - return state - } - - /** - * Delete forward `n` characters at the current selection. - * - * @param {Number} n (optional) - * @return {State} state - */ - - deleteForward(n = 1) { - let state = this - let { document, selection } = state - let { startKey } = selection - let after = selection - - // Determine what the selection should be after deleting. - const block = document.getClosestBlock(startKey) - const inline = document.getClosestInline(startKey) - - if (selection.isExpanded) { - after = selection.collapseToStart() - } - - else if ((block && block.isVoid) || (inline && inline.isVoid)) { - const next = document.getNextText(startKey) - const previous = document.getPreviousText(startKey) - after = next - ? selection.collapseToStartOf(next) - : selection.collapseToEndOf(previous) - } - - // Delete forward and then update the selection. - document = document.deleteForwardAtRange(selection) - selection = after - state = state.merge({ document, selection }) - return state - } - - /** - * Insert a `fragment` at the current selection. - * - * @param {List} fragment - * @return {State} state - */ - - insertFragment(fragment) { - let state = this - let { document, selection } = state - let after = selection - - // If there's nothing in the fragment, do nothing. - if (!fragment.length) return state - - // Lookup some nodes for determining the selection next. - const texts = fragment.getTextNodes() - const lastText = texts.last() - const lastInline = fragment.getClosestInline(lastText) - const startText = document.getDescendant(selection.startKey) - const startBlock = document.getClosestBlock(startText) - const startInline = document.getClosestInline(startText) - const nextText = document.getNextText(startText) - const nextBlock = nextText ? document.getClosestBlock(nextText) : null - const nextNextText = nextText ? document.getNextText(nextText) : null - - const docTexts = document.getTextNodes() - - // Insert the fragment. - document = document.insertFragmentAtRange(selection, fragment) - - // Determine what the selection should be after inserting. - const keys = docTexts.map(text => text.key) - const text = document.getTextNodes().findLast(n => !keys.includes(n.key)) - - after = text - ? selection.collapseToStartOf(text).moveForward(lastText.length) - : selection.collapseToStart().moveForward(lastText.length) - - // Update the document and selection. - selection = after - state = state.merge({ document, selection }) - return state - } - - /** - * Insert a `text` string at the current selection. - * - * @param {String} text - * @return {State} state - */ - - insertText(text) { - let state = this - let { document, selection } = state - let after = selection - - // Determine what the selection should be after inserting. - if (selection.isExpanded) { - after = selection.collapseToStart() - } - - // Insert the text and update the selection. - document = document.insertTextAtRange(selection, text) - selection = after - selection = selection.moveForward(text.length) - state = state.merge({ document, selection }) - return state - } - - /** - * Add a `mark` to the characters in the current selection. - * - * @param {Mark} mark - * @return {State} state - */ - - mark(mark) { - let state = this - let { document, selection } = state - document = document.markAtRange(selection, mark) - state = state.merge({ document }) - return state - } - - /** - * Move the selection to a specific anchor and focus point. - * - * @param {Object} properties - * @return {State} state - */ - - moveTo(properties) { - let state = this - let { document, selection } = state - - // Pass in properties, and force `isBackward` to be re-resolved. - selection = selection.merge({ - ...properties, - isBackward: null - }) - - selection = selection.normalize(document) - state = state.merge({ selection }) - return state - } - /** * Move the selection to the start of the previous block. * @@ -717,6 +518,233 @@ class State extends new Record(DEFAULTS) { return state } + /** + * Delete at the current selection. + * + * @return {State} state + */ + + delete() { + let state = this + let { document, selection } = state + + // When collapsed, there's nothing to do. + if (selection.isCollapsed) return state + + // Otherwise, delete and update the selection. + document = document.deleteAtRange(selection) + selection = selection.collapseToStart() + state = state.merge({ document, selection }) + return state + } + + /** + * Delete backward `n` characters at the current selection. + * + * @param {Number} n (optional) + * @return {State} state + */ + + deleteBackward(n = 1) { + let state = this + let { document, selection } = state + let after = selection + + // Determine what the selection should be after deleting. + const { startKey } = selection + const startNode = document.getDescendant(startKey) + + if (selection.isExpanded) { + after = selection.collapseToStart() + } + + else if (selection.isAtStartOf(document)) { + after = selection + } + + else if (selection.isAtStartOf(startNode)) { + const parent = document.getParent(startNode) + const previous = document.getPreviousSibling(parent).nodes.first() + after = selection.collapseToEndOf(previous) + } + + else { + after = selection.moveBackward(n) + } + + // Delete backward and then update the selection. + document = document.deleteBackwardAtRange(selection) + selection = after + state = state.merge({ document, selection }) + return state + } + + /** + * Delete forward `n` characters at the current selection. + * + * @param {Number} n (optional) + * @return {State} state + */ + + deleteForward(n = 1) { + let state = this + let { document, selection } = state + let { startKey } = selection + let after = selection + + // Determine what the selection should be after deleting. + const block = document.getClosestBlock(startKey) + const inline = document.getClosestInline(startKey) + + if (selection.isExpanded) { + after = selection.collapseToStart() + } + + else if ((block && block.isVoid) || (inline && inline.isVoid)) { + const next = document.getNextText(startKey) + const previous = document.getPreviousText(startKey) + after = next + ? selection.collapseToStartOf(next) + : selection.collapseToEndOf(previous) + } + + // Delete forward and then update the selection. + document = document.deleteForwardAtRange(selection) + selection = after + state = state.merge({ document, selection }) + return state + } + + /** + * Insert a `fragment` at the current selection. + * + * @param {List} fragment + * @return {State} state + */ + + insertFragment(fragment) { + let state = this + let { document, selection } = state + let after = selection + + // If there's nothing in the fragment, do nothing. + if (!fragment.length) return state + + // Lookup some nodes for determining the selection next. + const texts = fragment.getTextNodes() + const lastText = texts.last() + const lastInline = fragment.getClosestInline(lastText) + const startText = document.getDescendant(selection.startKey) + const startBlock = document.getClosestBlock(startText) + const startInline = document.getClosestInline(startText) + const nextText = document.getNextText(startText) + const nextBlock = nextText ? document.getClosestBlock(nextText) : null + const nextNextText = nextText ? document.getNextText(nextText) : null + + const docTexts = document.getTextNodes() + + // Insert the fragment. + document = document.insertFragmentAtRange(selection, fragment) + + // Determine what the selection should be after inserting. + const keys = docTexts.map(text => text.key) + const text = document.getTextNodes().findLast(n => !keys.includes(n.key)) + + after = text + ? selection.collapseToStartOf(text).moveForward(lastText.length) + : selection.collapseToStart().moveForward(lastText.length) + + // Update the document and selection. + selection = after + state = state.merge({ document, selection }) + return state + } + + /** + * Insert a `text` string at the current selection. + * + * @param {String} text + * @return {State} state + */ + + insertText(text) { + let state = this + let { cursorMarks, document, selection } = state + let after = selection + + // Determine what the selection should be after inserting. + if (selection.isExpanded) { + after = selection + .collapseToStart() + .moveForward(text.length) + } + + else { + after = selection.moveForward(text.length) + } + + // Insert the text. + document = document.insertTextAtRange(selection, text) + + // If there are any marks on the cursor, apply them. + if (cursorMarks.size) { + const range = after.extendBackward(text.length).normalize(document) + cursorMarks.forEach((mark) => { + document = document.markAtRange(range, mark) + }) + } + + // Update the selection and the state. + selection = after + state = state.merge({ document, selection }) + return state + } + + /** + * Add a `mark` to the characters in the current selection. + * + * @param {Mark} mark + * @return {State} state + */ + + mark(mark) { + let state = this + let { cursorMarks, document, selection } = state + + // If the selection is collapsed, add the mark to the cursor instead. + if (selection.isCollapsed) { + if (typeof mark == 'string') mark = new Mark({ type: mark }) + cursorMarks = cursorMarks.add(mark) + return state.merge({ cursorMarks }) + } + + document = document.markAtRange(selection, mark) + state = state.merge({ document }) + return state + } + + /** + * Move the selection to a specific anchor and focus point. + * + * @param {Object} properties + * @return {State} state + */ + + moveTo(properties) { + let state = this + let { document, selection } = state + + // Pass in properties, and force `isBackward` to be re-resolved. + selection = selection.merge({ + ...properties, + isBackward: null + }) + + selection = selection.normalize(document) + state = state.merge({ selection }) + return state + } + /** * Set `properties` of the block nodes in the current selection. * @@ -806,7 +834,15 @@ class State extends new Record(DEFAULTS) { unmark(mark) { let state = this - let { document, selection } = state + let { cursorMarks, document, selection } = state + + // If the selection is collapsed, remove the mark to the cursor instead. + if (selection.isCollapsed) { + if (typeof mark == 'string') mark = new Mark({ type: mark }) + cursorMarks = cursorMarks.remove(mark) + return state.merge({ cursorMarks }) + } + document = document.unmarkAtRange(selection, mark) state = state.merge({ document }) return state diff --git a/lib/models/transform.js b/lib/models/transform.js index ad70f6509..2b49d61bb 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -145,7 +145,7 @@ class Transform extends new Record(DEFAULT_PROPERTIES) { apply(options = {}) { const transform = this let { state, steps } = transform - let { history } = state + let { cursorMarks, history, selection } = state let { undos, redos } = history // Determine whether we need to create a new snapshot. @@ -186,6 +186,12 @@ class Transform extends new Record(DEFAULT_PROPERTIES) { // Apply each of the steps in the transform, arriving at a new state. state = steps.reduce((memo, step) => this.applyStep(memo, step), state) + // If the selection has changed, clear any existing cursor marks. + if (state.selection != selection) { + cursorMarks = cursorMarks.clear() + state = state.merge({ cursorMarks }) + } + // Apply the "isNative" flag, which is used to allow for natively-handled // content changes to skip rerendering the editor for performance. state = state.merge({ diff --git a/test/transforms/fixtures/mark/across-blocks/index.js b/test/transforms/fixtures/mark/across-blocks/index.js new file mode 100644 index 000000000..4b18c0457 --- /dev/null +++ b/test/transforms/fixtures/mark/across-blocks/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/across-blocks/input.yaml b/test/transforms/fixtures/mark/across-blocks/input.yaml new file mode 100644 index 000000000..4960c06f9 --- /dev/null +++ b/test/transforms/fixtures/mark/across-blocks/input.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: another diff --git a/test/transforms/fixtures/mark/across-blocks/output.yaml b/test/transforms/fixtures/mark/across-blocks/output.yaml new file mode 100644 index 000000000..e28155de5 --- /dev/null +++ b/test/transforms/fixtures/mark/across-blocks/output.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + - text: rd + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: an + marks: + - type: bold + - text: other diff --git a/test/transforms/fixtures/mark/across-inlines/index.js b/test/transforms/fixtures/mark/across-inlines/index.js new file mode 100644 index 000000000..4b18c0457 --- /dev/null +++ b/test/transforms/fixtures/mark/across-inlines/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + const second = texts.last() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 2, + focusKey: second.key, + focusOffset: 2 + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/across-inlines/input.yaml b/test/transforms/fixtures/mark/across-inlines/input.yaml new file mode 100644 index 000000000..c31f583e3 --- /dev/null +++ b/test/transforms/fixtures/mark/across-inlines/input.yaml @@ -0,0 +1,20 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: word + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: another diff --git a/test/transforms/fixtures/mark/across-inlines/output.yaml b/test/transforms/fixtures/mark/across-inlines/output.yaml new file mode 100644 index 000000000..7bf3a7a16 --- /dev/null +++ b/test/transforms/fixtures/mark/across-inlines/output.yaml @@ -0,0 +1,26 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: wo + - text: rd + marks: + - type: bold + - kind: block + type: paragraph + nodes: + - kind: inline + type: link + nodes: + - kind: text + ranges: + - text: an + marks: + - type: bold + - text: other diff --git a/test/transforms/fixtures/mark/collapsed-selection/index.js b/test/transforms/fixtures/mark/collapsed-selection/index.js new file mode 100644 index 000000000..ba6c09e19 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection/index.js @@ -0,0 +1,18 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 0 + }) + .mark('bold') + .insertText('a') + .apply() +} diff --git a/test/transforms/fixtures/mark/collapsed-selection/input.yaml b/test/transforms/fixtures/mark/collapsed-selection/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/collapsed-selection/output.yaml b/test/transforms/fixtures/mark/collapsed-selection/output.yaml new file mode 100644 index 000000000..5c2c51ad9 --- /dev/null +++ b/test/transforms/fixtures/mark/collapsed-selection/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: a + marks: + - type: bold + - text: word diff --git a/test/transforms/fixtures/mark/existing-marks/index.js b/test/transforms/fixtures/mark/existing-marks/index.js new file mode 100644 index 000000000..b28f088e8 --- /dev/null +++ b/test/transforms/fixtures/mark/existing-marks/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 2 + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/existing-marks/input.yaml b/test/transforms/fixtures/mark/existing-marks/input.yaml new file mode 100644 index 000000000..057734ba1 --- /dev/null +++ b/test/transforms/fixtures/mark/existing-marks/input.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: italic diff --git a/test/transforms/fixtures/mark/existing-marks/output.yaml b/test/transforms/fixtures/mark/existing-marks/output.yaml new file mode 100644 index 000000000..178a19f03 --- /dev/null +++ b/test/transforms/fixtures/mark/existing-marks/output.yaml @@ -0,0 +1,14 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wo + marks: + - type: italic + - type: bold + - text: rd + marks: + - type: italic diff --git a/test/transforms/fixtures/mark/first-character/index.js b/test/transforms/fixtures/mark/first-character/index.js new file mode 100644 index 000000000..b7d99872b --- /dev/null +++ b/test/transforms/fixtures/mark/first-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/first-character/input.yaml b/test/transforms/fixtures/mark/first-character/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/first-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/first-character/output.yaml b/test/transforms/fixtures/mark/first-character/output.yaml new file mode 100644 index 000000000..01cb91f1a --- /dev/null +++ b/test/transforms/fixtures/mark/first-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord diff --git a/test/transforms/fixtures/mark/last-character/index.js b/test/transforms/fixtures/mark/last-character/index.js new file mode 100644 index 000000000..240a30cee --- /dev/null +++ b/test/transforms/fixtures/mark/last-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: first.length - 1, + focusKey: first.key, + focusOffset: first.length + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/last-character/input.yaml b/test/transforms/fixtures/mark/last-character/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/last-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/last-character/output.yaml b/test/transforms/fixtures/mark/last-character/output.yaml new file mode 100644 index 000000000..d9b72a0a7 --- /dev/null +++ b/test/transforms/fixtures/mark/last-character/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: wor + - text: d + marks: + - type: bold diff --git a/test/transforms/fixtures/mark/middle-character/index.js b/test/transforms/fixtures/mark/middle-character/index.js new file mode 100644 index 000000000..b29f9620b --- /dev/null +++ b/test/transforms/fixtures/mark/middle-character/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 1, + focusKey: first.key, + focusOffset: 2 + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/middle-character/input.yaml b/test/transforms/fixtures/mark/middle-character/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/middle-character/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/middle-character/output.yaml b/test/transforms/fixtures/mark/middle-character/output.yaml new file mode 100644 index 000000000..0a062933a --- /dev/null +++ b/test/transforms/fixtures/mark/middle-character/output.yaml @@ -0,0 +1,12 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + - text: o + marks: + - type: bold + - text: rd diff --git a/test/transforms/fixtures/mark/whole-word/index.js b/test/transforms/fixtures/mark/whole-word/index.js new file mode 100644 index 000000000..592dc8443 --- /dev/null +++ b/test/transforms/fixtures/mark/whole-word/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: first.length + }) + .mark('bold') + .apply() +} diff --git a/test/transforms/fixtures/mark/whole-word/input.yaml b/test/transforms/fixtures/mark/whole-word/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/whole-word/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/whole-word/output.yaml b/test/transforms/fixtures/mark/whole-word/output.yaml new file mode 100644 index 000000000..fce007c77 --- /dev/null +++ b/test/transforms/fixtures/mark/whole-word/output.yaml @@ -0,0 +1,10 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word + marks: + - type: bold diff --git a/test/transforms/fixtures/mark/with-data-object/index.js b/test/transforms/fixtures/mark/with-data-object/index.js new file mode 100644 index 000000000..a5c893467 --- /dev/null +++ b/test/transforms/fixtures/mark/with-data-object/index.js @@ -0,0 +1,17 @@ + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + .mark('bold', { key: 'value' }) + .apply() +} diff --git a/test/transforms/fixtures/mark/with-data-object/input.yaml b/test/transforms/fixtures/mark/with-data-object/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/with-data-object/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/with-data-object/output.yaml b/test/transforms/fixtures/mark/with-data-object/output.yaml new file mode 100644 index 000000000..01cb91f1a --- /dev/null +++ b/test/transforms/fixtures/mark/with-data-object/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord diff --git a/test/transforms/fixtures/mark/with-data/index.js b/test/transforms/fixtures/mark/with-data/index.js new file mode 100644 index 000000000..7c8263e1c --- /dev/null +++ b/test/transforms/fixtures/mark/with-data/index.js @@ -0,0 +1,19 @@ + +import { Data } from '../../../../..' + +export default function (state) { + const { document, selection } = state + const texts = document.getTextNodes() + const first = texts.first() + + return state + .transform() + .moveTo({ + anchorKey: first.key, + anchorOffset: 0, + focusKey: first.key, + focusOffset: 1 + }) + .mark('bold', Data.create({ key: 'value' })) + .apply() +} diff --git a/test/transforms/fixtures/mark/with-data/input.yaml b/test/transforms/fixtures/mark/with-data/input.yaml new file mode 100644 index 000000000..b1be31e90 --- /dev/null +++ b/test/transforms/fixtures/mark/with-data/input.yaml @@ -0,0 +1,8 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: word diff --git a/test/transforms/fixtures/mark/with-data/output.yaml b/test/transforms/fixtures/mark/with-data/output.yaml new file mode 100644 index 000000000..01cb91f1a --- /dev/null +++ b/test/transforms/fixtures/mark/with-data/output.yaml @@ -0,0 +1,11 @@ + +nodes: + - kind: block + type: paragraph + nodes: + - kind: text + ranges: + - text: w + marks: + - type: bold + - text: ord