mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-22 15:02:51 +02:00
fix selection marks
This commit is contained in:
@@ -1,42 +1,8 @@
|
||||
|
||||
import Operations from '../transforms/operations'
|
||||
import Transforms from '../transforms'
|
||||
import includes from 'lodash/includes'
|
||||
import xor from 'lodash/xor'
|
||||
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.
|
||||
*
|
||||
@@ -53,9 +19,9 @@ class Transform {
|
||||
|
||||
constructor(properties) {
|
||||
const { state } = properties
|
||||
this.initialState = state
|
||||
this.state = state
|
||||
this.operations = []
|
||||
this.transforms = []
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +64,7 @@ class Transform {
|
||||
*/
|
||||
|
||||
apply(options = {}) {
|
||||
let { state, operations } = this
|
||||
let { initialState, state, operations } = this
|
||||
let { history, selection } = state
|
||||
let { marks } = selection
|
||||
let { undos, redos } = history
|
||||
@@ -118,11 +84,11 @@ class Transform {
|
||||
state = state.merge({ history })
|
||||
}
|
||||
|
||||
// If there are cursor marks and they haven't changed, remove them.
|
||||
if (state.selection.marks && state.selection.marks == marks) {
|
||||
selection = selection.merge({ marks: null })
|
||||
state = state.merge({ selection })
|
||||
}
|
||||
// Check whether to remove the cursor marks.
|
||||
// if (options.snapshot !== false && initialState.selection.marks) {
|
||||
// selection = selection.merge({ marks: null })
|
||||
// state = state.merge({ selection })
|
||||
// }
|
||||
|
||||
// Apply the "isNative" flag, which is used to allow for natively-handled
|
||||
// content changes to skip rerendering the editor for performance.
|
||||
@@ -140,35 +106,28 @@ class Transform {
|
||||
*/
|
||||
|
||||
shouldSnapshot() {
|
||||
const { state, transforms } = this
|
||||
const { state, operations } = this
|
||||
const { history, selection } = state
|
||||
const { undos, redos } = history
|
||||
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 (!previous) return true
|
||||
|
||||
// If there is a previous state but the transforms are different, snapshot.
|
||||
const types = transforms.map(t => t.type)
|
||||
const prevTypes = previous.transforms.map(t => t.type)
|
||||
const diff = xor(types, prevTypes)
|
||||
if (diff.length) return true
|
||||
// If the only operations applied are selection operations, don't snapshot.
|
||||
const onlySelections = operations.every(op => op.type == 'set_selection')
|
||||
if (onlySelections) return false
|
||||
|
||||
// If the current transforms aren't one of the "combinable" types, snapshot.
|
||||
const allCombinable = (
|
||||
transforms.every(t => t.type == 'insertText') ||
|
||||
transforms.every(t => t.type == 'deleteForward') ||
|
||||
transforms.every(t => t.type == 'deleteBackward')
|
||||
)
|
||||
// If the current operations aren't one of the "combinable" types, snapshot.
|
||||
const onlyInsert = operations.every(op => op.type == 'insert_text')
|
||||
const onlyRemove = operations.every(op => op.type == 'remove_text')
|
||||
const onlyPrevInsert = previous.operations.every(op => op.type == 'insert_text')
|
||||
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, don't snapshot.
|
||||
return false
|
||||
// Otherwise, snapshot.
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,9 +137,9 @@ class Transform {
|
||||
*/
|
||||
|
||||
snapshot() {
|
||||
let { state, transforms, operations } = this
|
||||
let { document, selection } = state
|
||||
return { document, selection, transforms, operations }
|
||||
let { initialState, operations } = this
|
||||
let { document, selection } = initialState
|
||||
return { document, selection, operations }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,13 +220,14 @@ class Transform {
|
||||
|
||||
Object.keys(Transforms).forEach((type) => {
|
||||
Transform.prototype[type] = function (...args) {
|
||||
this.transforms.push({ type, args })
|
||||
return Transforms[type](this, ...args)
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Export.
|
||||
*
|
||||
* @type {Transform}
|
||||
*/
|
||||
|
||||
export default Transform
|
||||
|
@@ -15,13 +15,21 @@ export function addMark(transform, mark) {
|
||||
const { state } = transform
|
||||
const { document, selection } = state
|
||||
|
||||
if (selection.isCollapsed) {
|
||||
const marks = document.getMarksAtRange(selection).add(mark)
|
||||
if (selection.isExpanded) {
|
||||
return transform.addMarkAtRange(selection, mark)
|
||||
}
|
||||
|
||||
else if (selection.marks) {
|
||||
const marks = selection.marks.add(mark)
|
||||
const sel = selection.merge({ marks })
|
||||
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 { document, selection } = state
|
||||
|
||||
if (selection.isCollapsed) {
|
||||
const marks = document.getMarksAtRange(selection).remove(mark)
|
||||
if (selection.isExpanded) {
|
||||
return transform.removeMarkAtRange(selection, mark)
|
||||
}
|
||||
|
||||
else if (selection.marks) {
|
||||
const marks = selection.marks.remove(mark)
|
||||
const sel = selection.merge({ marks })
|
||||
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) {
|
||||
properties = Normalize.selectionProperties(properties)
|
||||
|
||||
const { state } = transform
|
||||
const { selection } = state
|
||||
const prevProps = {}
|
||||
|
||||
if (properties.marks == selection.marks) {
|
||||
properties.marks = null
|
||||
}
|
||||
|
||||
for (const k in properties) {
|
||||
prevProps[k] = selection[k]
|
||||
}
|
||||
|
@@ -1,6 +1,15 @@
|
||||
|
||||
import Debug from 'debug'
|
||||
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`.
|
||||
*
|
||||
@@ -10,6 +19,7 @@ import uid from '../utils/uid'
|
||||
*/
|
||||
|
||||
function addMark(state, operation) {
|
||||
debug('add_mark', operation)
|
||||
const { path, offset, length, mark } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -28,6 +38,7 @@ function addMark(state, operation) {
|
||||
*/
|
||||
|
||||
function insertNode(state, operation) {
|
||||
debug('insert_node', operation)
|
||||
const { path, index, node } = operation
|
||||
let { document } = state
|
||||
let parent = document.assertPath(path)
|
||||
@@ -48,6 +59,7 @@ function insertNode(state, operation) {
|
||||
*/
|
||||
|
||||
function insertText(state, operation) {
|
||||
debug('insert_text', operation)
|
||||
const { path, offset, text, marks } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -66,6 +78,7 @@ function insertText(state, operation) {
|
||||
*/
|
||||
|
||||
function moveNode(state, operation) {
|
||||
debug('move_node', operation)
|
||||
const { path, newPath, newIndex } = operation
|
||||
let { document } = state
|
||||
const node = document.assertPath(path)
|
||||
@@ -94,6 +107,7 @@ function moveNode(state, operation) {
|
||||
*/
|
||||
|
||||
function removeMark(state, operation) {
|
||||
debug('remove_mark', operation)
|
||||
const { path, offset, length, mark } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -112,6 +126,7 @@ function removeMark(state, operation) {
|
||||
*/
|
||||
|
||||
function removeNode(state, operation) {
|
||||
debug('remove_node', operation)
|
||||
const { path } = operation
|
||||
let { document } = state
|
||||
const node = document.assertPath(path)
|
||||
@@ -134,6 +149,7 @@ function removeNode(state, operation) {
|
||||
*/
|
||||
|
||||
function removeText(state, operation) {
|
||||
debug('remove_text', operation)
|
||||
const { path, offset, length } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -153,6 +169,7 @@ function removeText(state, operation) {
|
||||
*/
|
||||
|
||||
function setMark(state, operation) {
|
||||
debug('set_mark', operation)
|
||||
const { path, offset, length, mark, properties } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -171,6 +188,7 @@ function setMark(state, operation) {
|
||||
*/
|
||||
|
||||
function setNode(state, operation) {
|
||||
debug('set_node', operation)
|
||||
const { path, properties } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
@@ -190,6 +208,7 @@ function setNode(state, operation) {
|
||||
*/
|
||||
|
||||
function setSelection(state, operation) {
|
||||
debug('set_selection', operation)
|
||||
let { properties } = operation
|
||||
let { selection } = state
|
||||
selection = selection.merge(properties)
|
||||
@@ -206,6 +225,7 @@ function setSelection(state, operation) {
|
||||
*/
|
||||
|
||||
function splitNode(state, operation) {
|
||||
debug('split_node', operation)
|
||||
const { path, offset } = operation
|
||||
let { document } = state
|
||||
let node = document.assertPath(path)
|
||||
|
@@ -188,12 +188,13 @@ function selectionProperties(value = {}) {
|
||||
|
||||
switch (typeOf(value)) {
|
||||
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.anchorOffset != null) ret.anchorOffset = value.anchorOffset
|
||||
if (value.focusKey != null) ret.focusKey = value.focusKey
|
||||
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
|
||||
}
|
||||
default: {
|
||||
|
Reference in New Issue
Block a user