mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-22 23:12:52 +02:00
fix selection marks
This commit is contained in:
@@ -1,42 +1,8 @@
|
|||||||
|
|
||||||
import Operations from '../transforms/operations'
|
import Operations from '../transforms/operations'
|
||||||
import Transforms from '../transforms'
|
import Transforms from '../transforms'
|
||||||
import includes from 'lodash/includes'
|
|
||||||
import xor from 'lodash/xor'
|
|
||||||
import { List, Record } from 'immutable'
|
import { List, Record } from 'immutable'
|
||||||
|
|
||||||
/**
|
|
||||||
* Selection transforms.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const SELECTION_TRANSFORMS = [
|
|
||||||
'blur',
|
|
||||||
'collapseToAnchor',
|
|
||||||
'collapseToEnd',
|
|
||||||
'collapseToEndOf',
|
|
||||||
'collapseToFocus',
|
|
||||||
'collapseToStart',
|
|
||||||
'collapseToStartOf',
|
|
||||||
'extendBackward',
|
|
||||||
'extendForward',
|
|
||||||
'extendToEndOf',
|
|
||||||
'extendToStartOf',
|
|
||||||
'focus',
|
|
||||||
'moveBackward',
|
|
||||||
'moveForward',
|
|
||||||
'moveToOffsets',
|
|
||||||
'moveToRangeOf',
|
|
||||||
'collapseToEndOfNextBlock',
|
|
||||||
'collapseToEndOfNextText',
|
|
||||||
'collapseToEndOfPreviousBlock',
|
|
||||||
'collapseToEndOfPreviousText',
|
|
||||||
'collapseToStartOfNextBlock',
|
|
||||||
'collapseToStartOfNextText',
|
|
||||||
'collapseToStartOfPreviousBlock',
|
|
||||||
'collapseToStartOfPreviousText',
|
|
||||||
'moveTo',
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform.
|
* Transform.
|
||||||
*
|
*
|
||||||
@@ -53,9 +19,9 @@ class Transform {
|
|||||||
|
|
||||||
constructor(properties) {
|
constructor(properties) {
|
||||||
const { state } = properties
|
const { state } = properties
|
||||||
|
this.initialState = state
|
||||||
this.state = state
|
this.state = state
|
||||||
this.operations = []
|
this.operations = []
|
||||||
this.transforms = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +64,7 @@ class Transform {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
apply(options = {}) {
|
apply(options = {}) {
|
||||||
let { state, operations } = this
|
let { initialState, state, operations } = this
|
||||||
let { history, selection } = state
|
let { history, selection } = state
|
||||||
let { marks } = selection
|
let { marks } = selection
|
||||||
let { undos, redos } = history
|
let { undos, redos } = history
|
||||||
@@ -118,11 +84,11 @@ class Transform {
|
|||||||
state = state.merge({ history })
|
state = state.merge({ history })
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are cursor marks and they haven't changed, remove them.
|
// Check whether to remove the cursor marks.
|
||||||
if (state.selection.marks && state.selection.marks == marks) {
|
// if (options.snapshot !== false && initialState.selection.marks) {
|
||||||
selection = selection.merge({ marks: null })
|
// selection = selection.merge({ marks: null })
|
||||||
state = state.merge({ selection })
|
// state = state.merge({ selection })
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -140,35 +106,28 @@ class Transform {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
shouldSnapshot() {
|
shouldSnapshot() {
|
||||||
const { state, transforms } = this
|
const { state, operations } = this
|
||||||
const { history, selection } = state
|
const { history, selection } = state
|
||||||
const { undos, redos } = history
|
const { undos, redos } = history
|
||||||
const previous = undos.peek()
|
const previous = undos.peek()
|
||||||
|
|
||||||
// If the only transforms applied are selection transforms, don't snapshot.
|
|
||||||
const onlySelections = transforms.every(t => includes(SELECTION_TRANSFORMS, t.type))
|
|
||||||
if (onlySelections) return false
|
|
||||||
|
|
||||||
// If there isn't a previous state, snapshot.
|
// If there isn't a previous state, snapshot.
|
||||||
if (!previous) return true
|
if (!previous) return true
|
||||||
|
|
||||||
// If there is a previous state but the transforms are different, snapshot.
|
// If the only operations applied are selection operations, don't snapshot.
|
||||||
const types = transforms.map(t => t.type)
|
const onlySelections = operations.every(op => op.type == 'set_selection')
|
||||||
const prevTypes = previous.transforms.map(t => t.type)
|
if (onlySelections) return false
|
||||||
const diff = xor(types, prevTypes)
|
|
||||||
if (diff.length) return true
|
|
||||||
|
|
||||||
// If the current transforms aren't one of the "combinable" types, snapshot.
|
// If the current operations aren't one of the "combinable" types, snapshot.
|
||||||
const allCombinable = (
|
const onlyInsert = operations.every(op => op.type == 'insert_text')
|
||||||
transforms.every(t => t.type == 'insertText') ||
|
const onlyRemove = operations.every(op => op.type == 'remove_text')
|
||||||
transforms.every(t => t.type == 'deleteForward') ||
|
const onlyPrevInsert = previous.operations.every(op => op.type == 'insert_text')
|
||||||
transforms.every(t => t.type == 'deleteBackward')
|
const onlyPrevRemove = previous.operations.every(op => op.type == 'remove_text')
|
||||||
)
|
if (onlyInsert && onlyPrevInsert) return false
|
||||||
|
if (onlyRemove && onlyPrevRemove) return false
|
||||||
|
|
||||||
if (!allCombinable) return true
|
// Otherwise, snapshot.
|
||||||
|
return true
|
||||||
// Otherwise, don't snapshot.
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,9 +137,9 @@ class Transform {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
snapshot() {
|
snapshot() {
|
||||||
let { state, transforms, operations } = this
|
let { initialState, operations } = this
|
||||||
let { document, selection } = state
|
let { document, selection } = initialState
|
||||||
return { document, selection, transforms, operations }
|
return { document, selection, operations }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,13 +220,14 @@ class Transform {
|
|||||||
|
|
||||||
Object.keys(Transforms).forEach((type) => {
|
Object.keys(Transforms).forEach((type) => {
|
||||||
Transform.prototype[type] = function (...args) {
|
Transform.prototype[type] = function (...args) {
|
||||||
this.transforms.push({ type, args })
|
|
||||||
return Transforms[type](this, ...args)
|
return Transforms[type](this, ...args)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export.
|
* Export.
|
||||||
|
*
|
||||||
|
* @type {Transform}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default Transform
|
export default Transform
|
||||||
|
@@ -15,13 +15,21 @@ export function addMark(transform, mark) {
|
|||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { document, selection } = state
|
const { document, selection } = state
|
||||||
|
|
||||||
if (selection.isCollapsed) {
|
if (selection.isExpanded) {
|
||||||
const marks = document.getMarksAtRange(selection).add(mark)
|
return transform.addMarkAtRange(selection, mark)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (selection.marks) {
|
||||||
|
const marks = selection.marks.add(mark)
|
||||||
const sel = selection.merge({ marks })
|
const sel = selection.merge({ marks })
|
||||||
return transform.setSelection(sel)
|
return transform.setSelection(sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transform.addMarkAtRange(selection, mark)
|
else {
|
||||||
|
const marks = document.getMarksAtRange(selection).add(mark)
|
||||||
|
const sel = selection.merge({ marks })
|
||||||
|
return transform.setSelection(sel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -445,13 +453,21 @@ export function removeMark(transform, mark) {
|
|||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { document, selection } = state
|
const { document, selection } = state
|
||||||
|
|
||||||
if (selection.isCollapsed) {
|
if (selection.isExpanded) {
|
||||||
const marks = document.getMarksAtRange(selection).remove(mark)
|
return transform.removeMarkAtRange(selection, mark)
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (selection.marks) {
|
||||||
|
const marks = selection.marks.remove(mark)
|
||||||
const sel = selection.merge({ marks })
|
const sel = selection.merge({ marks })
|
||||||
return transform.setSelection(sel)
|
return transform.setSelection(sel)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transform.removeMarkAtRange(selection, mark)
|
else {
|
||||||
|
const marks = document.getMarksAtRange(selection).remove(mark)
|
||||||
|
const sel = selection.merge({ marks })
|
||||||
|
return transform.setSelection(sel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -416,10 +416,15 @@ export function moveToRangeOf(transform, start, end) {
|
|||||||
|
|
||||||
export function setSelection(transform, properties) {
|
export function setSelection(transform, properties) {
|
||||||
properties = Normalize.selectionProperties(properties)
|
properties = Normalize.selectionProperties(properties)
|
||||||
|
|
||||||
const { state } = transform
|
const { state } = transform
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
const prevProps = {}
|
const prevProps = {}
|
||||||
|
|
||||||
|
if (properties.marks == selection.marks) {
|
||||||
|
properties.marks = null
|
||||||
|
}
|
||||||
|
|
||||||
for (const k in properties) {
|
for (const k in properties) {
|
||||||
prevProps[k] = selection[k]
|
prevProps[k] = selection[k]
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,15 @@
|
|||||||
|
|
||||||
|
import Debug from 'debug'
|
||||||
import uid from '../utils/uid'
|
import uid from '../utils/uid'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug.
|
||||||
|
*
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const debug = Debug('slate:operation')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add mark to text at `offset` and `length` in node by `path`.
|
* Add mark to text at `offset` and `length` in node by `path`.
|
||||||
*
|
*
|
||||||
@@ -10,6 +19,7 @@ import uid from '../utils/uid'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function addMark(state, operation) {
|
function addMark(state, operation) {
|
||||||
|
debug('add_mark', operation)
|
||||||
const { path, offset, length, mark } = operation
|
const { path, offset, length, mark } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -28,6 +38,7 @@ function addMark(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function insertNode(state, operation) {
|
function insertNode(state, operation) {
|
||||||
|
debug('insert_node', operation)
|
||||||
const { path, index, node } = operation
|
const { path, index, node } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let parent = document.assertPath(path)
|
let parent = document.assertPath(path)
|
||||||
@@ -48,6 +59,7 @@ function insertNode(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function insertText(state, operation) {
|
function insertText(state, operation) {
|
||||||
|
debug('insert_text', operation)
|
||||||
const { path, offset, text, marks } = operation
|
const { path, offset, text, marks } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -66,6 +78,7 @@ function insertText(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function moveNode(state, operation) {
|
function moveNode(state, operation) {
|
||||||
|
debug('move_node', operation)
|
||||||
const { path, newPath, newIndex } = operation
|
const { path, newPath, newIndex } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
const node = document.assertPath(path)
|
const node = document.assertPath(path)
|
||||||
@@ -94,6 +107,7 @@ function moveNode(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function removeMark(state, operation) {
|
function removeMark(state, operation) {
|
||||||
|
debug('remove_mark', operation)
|
||||||
const { path, offset, length, mark } = operation
|
const { path, offset, length, mark } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -112,6 +126,7 @@ function removeMark(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function removeNode(state, operation) {
|
function removeNode(state, operation) {
|
||||||
|
debug('remove_node', operation)
|
||||||
const { path } = operation
|
const { path } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
const node = document.assertPath(path)
|
const node = document.assertPath(path)
|
||||||
@@ -134,6 +149,7 @@ function removeNode(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function removeText(state, operation) {
|
function removeText(state, operation) {
|
||||||
|
debug('remove_text', operation)
|
||||||
const { path, offset, length } = operation
|
const { path, offset, length } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -153,6 +169,7 @@ function removeText(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function setMark(state, operation) {
|
function setMark(state, operation) {
|
||||||
|
debug('set_mark', operation)
|
||||||
const { path, offset, length, mark, properties } = operation
|
const { path, offset, length, mark, properties } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -171,6 +188,7 @@ function setMark(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function setNode(state, operation) {
|
function setNode(state, operation) {
|
||||||
|
debug('set_node', operation)
|
||||||
const { path, properties } = operation
|
const { path, properties } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
@@ -190,6 +208,7 @@ function setNode(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function setSelection(state, operation) {
|
function setSelection(state, operation) {
|
||||||
|
debug('set_selection', operation)
|
||||||
let { properties } = operation
|
let { properties } = operation
|
||||||
let { selection } = state
|
let { selection } = state
|
||||||
selection = selection.merge(properties)
|
selection = selection.merge(properties)
|
||||||
@@ -206,6 +225,7 @@ function setSelection(state, operation) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function splitNode(state, operation) {
|
function splitNode(state, operation) {
|
||||||
|
debug('split_node', operation)
|
||||||
const { path, offset } = operation
|
const { path, offset } = operation
|
||||||
let { document } = state
|
let { document } = state
|
||||||
let node = document.assertPath(path)
|
let node = document.assertPath(path)
|
||||||
|
@@ -188,12 +188,13 @@ function selectionProperties(value = {}) {
|
|||||||
|
|
||||||
switch (typeOf(value)) {
|
switch (typeOf(value)) {
|
||||||
case 'object': {
|
case 'object': {
|
||||||
if (value.isFocused != null) ret.isFocused = !!value.isFocused
|
|
||||||
if (value.isBackward != null) ret.isBackward = !!value.isBackward
|
|
||||||
if (value.anchorKey != null) ret.anchorKey = value.anchorKey
|
if (value.anchorKey != null) ret.anchorKey = value.anchorKey
|
||||||
if (value.anchorOffset != null) ret.anchorOffset = value.anchorOffset
|
if (value.anchorOffset != null) ret.anchorOffset = value.anchorOffset
|
||||||
if (value.focusKey != null) ret.focusKey = value.focusKey
|
if (value.focusKey != null) ret.focusKey = value.focusKey
|
||||||
if (value.focusOffset != null) ret.focusOffset = value.focusOffset
|
if (value.focusOffset != null) ret.focusOffset = value.focusOffset
|
||||||
|
if (value.isBackward != null) ret.isBackward = !!value.isBackward
|
||||||
|
if (value.isFocused != null) ret.isFocused = !!value.isFocused
|
||||||
|
if (value.marks !== undefined) ret.marks = value.marks
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
Reference in New Issue
Block a user