mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-01-18 05:59:13 +01:00
Change how slate-history handles selection undo (#4717)
* Change how slate-history handles selection undo * fix test * fix lint * cleanup and simplify * Fix redo by applying undo beforeSelection before applying the redo * remove unused shouldClear function * fix lint * add changeset
This commit is contained in:
parent
4e52e5043e
commit
d73026eed2
5
.changeset/cuddly-kiwis-work.md
Normal file
5
.changeset/cuddly-kiwis-work.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'slate-history': minor
|
||||
---
|
||||
|
||||
Changes how selections are stored in the history resulting in more consistent results
|
@ -1,5 +1,10 @@
|
||||
import { isPlainObject } from 'is-plain-object'
|
||||
import { Operation } from 'slate'
|
||||
import { Operation, Range } from 'slate'
|
||||
|
||||
interface Batch {
|
||||
operations: Operation[]
|
||||
selectionBefore: Range | null
|
||||
}
|
||||
|
||||
/**
|
||||
* `History` objects hold all of the operations that are applied to a value, so
|
||||
@ -7,8 +12,8 @@ import { Operation } from 'slate'
|
||||
*/
|
||||
|
||||
export interface History {
|
||||
redos: Operation[][]
|
||||
undos: Operation[][]
|
||||
redos: Batch[]
|
||||
undos: Batch[]
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-redeclare
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Editor, Operation, Path } from 'slate'
|
||||
import { Editor, Operation, Path, Range, Transforms } from 'slate'
|
||||
|
||||
import { HistoryEditor } from './history-editor'
|
||||
|
||||
@ -24,9 +24,13 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
if (redos.length > 0) {
|
||||
const batch = redos[redos.length - 1]
|
||||
|
||||
if (batch.selectionBefore) {
|
||||
Transforms.setSelection(e, batch.selectionBefore)
|
||||
}
|
||||
|
||||
HistoryEditor.withoutSaving(e, () => {
|
||||
Editor.withoutNormalizing(e, () => {
|
||||
for (const op of batch) {
|
||||
for (const op of batch.operations) {
|
||||
e.apply(op)
|
||||
}
|
||||
})
|
||||
@ -46,11 +50,14 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
|
||||
HistoryEditor.withoutSaving(e, () => {
|
||||
Editor.withoutNormalizing(e, () => {
|
||||
const inverseOps = batch.map(Operation.inverse).reverse()
|
||||
const inverseOps = batch.operations.map(Operation.inverse).reverse()
|
||||
|
||||
for (const op of inverseOps) {
|
||||
e.apply(op)
|
||||
}
|
||||
if (batch.selectionBefore) {
|
||||
Transforms.setSelection(e, batch.selectionBefore)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -63,8 +70,8 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
const { operations, history } = e
|
||||
const { undos } = history
|
||||
const lastBatch = undos[undos.length - 1]
|
||||
const lastOp = lastBatch && lastBatch[lastBatch.length - 1]
|
||||
const overwrite = shouldOverwrite(op, lastOp)
|
||||
const lastOp =
|
||||
lastBatch && lastBatch.operations[lastBatch.operations.length - 1]
|
||||
let save = HistoryEditor.isSaving(e)
|
||||
let merge = HistoryEditor.isMerging(e)
|
||||
|
||||
@ -79,18 +86,17 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
} else if (operations.length !== 0) {
|
||||
merge = true
|
||||
} else {
|
||||
merge = shouldMerge(op, lastOp) || overwrite
|
||||
merge = shouldMerge(op, lastOp)
|
||||
}
|
||||
}
|
||||
|
||||
if (lastBatch && merge) {
|
||||
if (overwrite) {
|
||||
lastBatch.pop()
|
||||
}
|
||||
|
||||
lastBatch.push(op)
|
||||
lastBatch.operations.push(op)
|
||||
} else {
|
||||
const batch = [op]
|
||||
const batch = {
|
||||
operations: [op],
|
||||
selectionBefore: e.selection,
|
||||
}
|
||||
undos.push(batch)
|
||||
}
|
||||
|
||||
@ -98,9 +104,7 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
undos.shift()
|
||||
}
|
||||
|
||||
if (shouldClear(op)) {
|
||||
history.redos = []
|
||||
}
|
||||
history.redos = []
|
||||
}
|
||||
|
||||
apply(op)
|
||||
@ -114,10 +118,6 @@ export const withHistory = <T extends Editor>(editor: T) => {
|
||||
*/
|
||||
|
||||
const shouldMerge = (op: Operation, prev: Operation | undefined): boolean => {
|
||||
if (op.type === 'set_selection') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
prev &&
|
||||
op.type === 'insert_text' &&
|
||||
@ -146,36 +146,6 @@ const shouldMerge = (op: Operation, prev: Operation | undefined): boolean => {
|
||||
*/
|
||||
|
||||
const shouldSave = (op: Operation, prev: Operation | undefined): boolean => {
|
||||
if (
|
||||
op.type === 'set_selection' &&
|
||||
(op.properties == null || op.newProperties == null)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an operation should overwrite the previous one.
|
||||
*/
|
||||
|
||||
const shouldOverwrite = (
|
||||
op: Operation,
|
||||
prev: Operation | undefined
|
||||
): boolean => {
|
||||
if (prev && op.type === 'set_selection' && prev.type === 'set_selection') {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether an operation should clear the redos stack.
|
||||
*/
|
||||
|
||||
const shouldClear = (op: Operation): boolean => {
|
||||
if (op.type === 'set_selection') {
|
||||
return false
|
||||
}
|
||||
|
@ -44,6 +44,6 @@ export const output = {
|
||||
],
|
||||
selection: {
|
||||
anchor: { path: [0, 0], offset: 5 },
|
||||
focus: { path: [0, 0], offset: 5 },
|
||||
focus: { path: [0, 0], offset: 0 },
|
||||
},
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user