mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-28 09:29:49 +02:00
add support for collapsed cursor marks, fixes #82
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
import Document from './document'
|
import Document from './document'
|
||||||
|
import Mark from './mark'
|
||||||
import Selection from './selection'
|
import Selection from './selection'
|
||||||
import Transform from './transform'
|
import Transform from './transform'
|
||||||
import uid from '../utils/uid'
|
import uid from '../utils/uid'
|
||||||
import { Record, Stack } from 'immutable'
|
import { Record, Set, Stack } from 'immutable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* History.
|
* History.
|
||||||
@@ -20,6 +21,7 @@ const History = new Record({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
|
cursorMarks: new Set(),
|
||||||
document: new Document(),
|
document: new Document(),
|
||||||
selection: new Selection(),
|
selection: new Selection(),
|
||||||
history: new History(),
|
history: new History(),
|
||||||
@@ -283,7 +285,10 @@ class State extends new Record(DEFAULTS) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
get marks() {
|
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 })
|
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.
|
* Move the selection to the start of the previous block.
|
||||||
*
|
*
|
||||||
@@ -717,6 +518,233 @@ class State extends new Record(DEFAULTS) {
|
|||||||
return state
|
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.
|
* Set `properties` of the block nodes in the current selection.
|
||||||
*
|
*
|
||||||
@@ -806,7 +834,15 @@ class State extends new Record(DEFAULTS) {
|
|||||||
|
|
||||||
unmark(mark) {
|
unmark(mark) {
|
||||||
let state = this
|
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)
|
document = document.unmarkAtRange(selection, mark)
|
||||||
state = state.merge({ document })
|
state = state.merge({ document })
|
||||||
return state
|
return state
|
||||||
|
@@ -145,7 +145,7 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
|
|||||||
apply(options = {}) {
|
apply(options = {}) {
|
||||||
const transform = this
|
const transform = this
|
||||||
let { state, steps } = transform
|
let { state, steps } = transform
|
||||||
let { history } = state
|
let { cursorMarks, history, selection } = state
|
||||||
let { undos, redos } = history
|
let { undos, redos } = history
|
||||||
|
|
||||||
// Determine whether we need to create a new snapshot.
|
// 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.
|
// Apply each of the steps in the transform, arriving at a new state.
|
||||||
state = steps.reduce((memo, step) => this.applyStep(memo, step), 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
|
// Apply the "isNative" flag, which is used to allow for natively-handled
|
||||||
// content changes to skip rerendering the editor for performance.
|
// content changes to skip rerendering the editor for performance.
|
||||||
state = state.merge({
|
state = state.merge({
|
||||||
|
18
test/transforms/fixtures/mark/across-blocks/index.js
Normal file
18
test/transforms/fixtures/mark/across-blocks/index.js
Normal file
@@ -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()
|
||||||
|
}
|
14
test/transforms/fixtures/mark/across-blocks/input.yaml
Normal file
14
test/transforms/fixtures/mark/across-blocks/input.yaml
Normal file
@@ -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
|
20
test/transforms/fixtures/mark/across-blocks/output.yaml
Normal file
20
test/transforms/fixtures/mark/across-blocks/output.yaml
Normal file
@@ -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
|
18
test/transforms/fixtures/mark/across-inlines/index.js
Normal file
18
test/transforms/fixtures/mark/across-inlines/index.js
Normal file
@@ -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()
|
||||||
|
}
|
20
test/transforms/fixtures/mark/across-inlines/input.yaml
Normal file
20
test/transforms/fixtures/mark/across-inlines/input.yaml
Normal file
@@ -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
|
26
test/transforms/fixtures/mark/across-inlines/output.yaml
Normal file
26
test/transforms/fixtures/mark/across-inlines/output.yaml
Normal file
@@ -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
|
18
test/transforms/fixtures/mark/collapsed-selection/index.js
Normal file
18
test/transforms/fixtures/mark/collapsed-selection/index.js
Normal file
@@ -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()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: a
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: word
|
17
test/transforms/fixtures/mark/existing-marks/index.js
Normal file
17
test/transforms/fixtures/mark/existing-marks/index.js
Normal file
@@ -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()
|
||||||
|
}
|
10
test/transforms/fixtures/mark/existing-marks/input.yaml
Normal file
10
test/transforms/fixtures/mark/existing-marks/input.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
marks:
|
||||||
|
- type: italic
|
14
test/transforms/fixtures/mark/existing-marks/output.yaml
Normal file
14
test/transforms/fixtures/mark/existing-marks/output.yaml
Normal file
@@ -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
|
17
test/transforms/fixtures/mark/first-character/index.js
Normal file
17
test/transforms/fixtures/mark/first-character/index.js
Normal file
@@ -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()
|
||||||
|
}
|
8
test/transforms/fixtures/mark/first-character/input.yaml
Normal file
8
test/transforms/fixtures/mark/first-character/input.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
11
test/transforms/fixtures/mark/first-character/output.yaml
Normal file
11
test/transforms/fixtures/mark/first-character/output.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: ord
|
17
test/transforms/fixtures/mark/last-character/index.js
Normal file
17
test/transforms/fixtures/mark/last-character/index.js
Normal file
@@ -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()
|
||||||
|
}
|
8
test/transforms/fixtures/mark/last-character/input.yaml
Normal file
8
test/transforms/fixtures/mark/last-character/input.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
11
test/transforms/fixtures/mark/last-character/output.yaml
Normal file
11
test/transforms/fixtures/mark/last-character/output.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: wor
|
||||||
|
- text: d
|
||||||
|
marks:
|
||||||
|
- type: bold
|
17
test/transforms/fixtures/mark/middle-character/index.js
Normal file
17
test/transforms/fixtures/mark/middle-character/index.js
Normal file
@@ -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()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
12
test/transforms/fixtures/mark/middle-character/output.yaml
Normal file
12
test/transforms/fixtures/mark/middle-character/output.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
- text: o
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: rd
|
17
test/transforms/fixtures/mark/whole-word/index.js
Normal file
17
test/transforms/fixtures/mark/whole-word/index.js
Normal file
@@ -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()
|
||||||
|
}
|
8
test/transforms/fixtures/mark/whole-word/input.yaml
Normal file
8
test/transforms/fixtures/mark/whole-word/input.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
10
test/transforms/fixtures/mark/whole-word/output.yaml
Normal file
10
test/transforms/fixtures/mark/whole-word/output.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
||||||
|
marks:
|
||||||
|
- type: bold
|
17
test/transforms/fixtures/mark/with-data-object/index.js
Normal file
17
test/transforms/fixtures/mark/with-data-object/index.js
Normal file
@@ -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()
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
11
test/transforms/fixtures/mark/with-data-object/output.yaml
Normal file
11
test/transforms/fixtures/mark/with-data-object/output.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: ord
|
19
test/transforms/fixtures/mark/with-data/index.js
Normal file
19
test/transforms/fixtures/mark/with-data/index.js
Normal file
@@ -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()
|
||||||
|
}
|
8
test/transforms/fixtures/mark/with-data/input.yaml
Normal file
8
test/transforms/fixtures/mark/with-data/input.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: word
|
11
test/transforms/fixtures/mark/with-data/output.yaml
Normal file
11
test/transforms/fixtures/mark/with-data/output.yaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
nodes:
|
||||||
|
- kind: block
|
||||||
|
type: paragraph
|
||||||
|
nodes:
|
||||||
|
- kind: text
|
||||||
|
ranges:
|
||||||
|
- text: w
|
||||||
|
marks:
|
||||||
|
- type: bold
|
||||||
|
- text: ord
|
Reference in New Issue
Block a user