mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-27 09:04:31 +02:00
add support for collapsed cursor marks, fixes #82
This commit is contained in:
@@ -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
|
||||
|
@@ -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({
|
||||
|
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