Skip to content

Commit e534deb

Browse files
authored
Merge pull request #183 from nperez0111/undo-redo-commands
Add `undoCommand` & `redoCommand` for prosemirror-history compatible API
2 parents 3021e2f + 1f43175 commit e534deb

File tree

3 files changed

+62
-11
lines changed

3 files changed

+62
-11
lines changed

src/plugins/keys.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const ySyncPluginKey = new PluginKey('y-sync')
1111
* The unique prosemirror plugin key for undoPlugin
1212
*
1313
* @public
14+
* @type {PluginKey<import('./undo-plugin').UndoPluginState>}
1415
*/
1516
export const yUndoPluginKey = new PluginKey('y-undo')
1617

src/plugins/sync-plugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import * as utils from '../utils.js'
3030
*/
3131

3232
/**
33-
* @return BindingMetadata
33+
* @return {BindingMetadata}
3434
*/
3535
export const createEmptyMeta = () => ({
3636
mapping: new Map(),

src/plugins/undo-plugin.js

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,86 @@
1-
import { Plugin } from 'prosemirror-state' // eslint-disable-line
1+
import { Plugin } from 'prosemirror-state'
22

33
import { getRelativeSelection } from './sync-plugin.js'
44
import { UndoManager, Item, ContentType, XmlElement, Text } from 'yjs'
55
import { yUndoPluginKey, ySyncPluginKey } from './keys.js'
66

7-
export const undo = state => {
7+
/**
8+
* @typedef {Object} UndoPluginState
9+
* @property {import('yjs').UndoManager} undoManager
10+
* @property {ReturnType<typeof getRelativeSelection> | null} prevSel
11+
* @property {boolean} hasUndoOps
12+
* @property {boolean} hasRedoOps
13+
*/
14+
15+
/**
16+
* Undo the last user action if there are undo operations available
17+
* @type {import('prosemirror-state').Command}
18+
*/
19+
export const undoCommand = (state, dispatch) => {
820
const undoManager = yUndoPluginKey.getState(state).undoManager
9-
if (undoManager != null) {
21+
if (undoManager == null || !undoManager.undoStack.length) {
22+
return false
23+
}
24+
25+
if (dispatch) {
1026
undoManager.undo()
11-
return true
1227
}
28+
return true
1329
}
1430

15-
export const redo = state => {
31+
/**
32+
* Redo the last user action if there are redo operations available
33+
* @type {import('prosemirror-state').Command}
34+
*/
35+
export const redoCommand = (state, dispatch) => {
1636
const undoManager = yUndoPluginKey.getState(state).undoManager
17-
if (undoManager != null) {
37+
if (undoManager == null || !undoManager.redoStack.length) {
38+
return false
39+
}
40+
41+
if (dispatch) {
1842
undoManager.redo()
19-
return true
2043
}
44+
return true
45+
}
46+
47+
/**
48+
* Undo the last user action
49+
* @param {import('prosemirror-state').EditorState} state
50+
* @returns {boolean}
51+
*/
52+
export const undo = (state) => {
53+
return undoCommand(state, () => {})
54+
}
55+
56+
/**
57+
* Redo the last user action
58+
* @param {import('prosemirror-state').EditorState} state
59+
* @returns {boolean}
60+
*/
61+
export const redo = (state) => {
62+
return redoCommand(state, () => {})
2163
}
2264

2365
export const defaultProtectedNodes = new Set(['paragraph'])
2466

67+
/**
68+
* @param {import('yjs').Item} item
69+
* @param {Set<string>} protectedNodes
70+
* @returns {boolean}
71+
*/
2572
export const defaultDeleteFilter = (item, protectedNodes) => !(item instanceof Item) ||
2673
!(item.content instanceof ContentType) ||
2774
!(item.content.type instanceof Text ||
2875
(item.content.type instanceof XmlElement && protectedNodes.has(item.content.type.nodeName))) ||
2976
item.content.type._length === 0
3077

78+
/**
79+
* @param {object} [options]
80+
* @param {Set<string>} [options.protectedNodes]
81+
* @param {any[]} [options.trackedOrigins]
82+
* @param {import('yjs').UndoManager | null} [options.undoManager]
83+
*/
3184
export const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) => new Plugin({
3285
key: yUndoPluginKey,
3386
state: {
@@ -46,9 +99,6 @@ export const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOri
4699
hasRedoOps: _undoManager.redoStack.length > 0
47100
}
48101
},
49-
/**
50-
* @returns {any}
51-
*/
52102
apply: (tr, val, oldState, state) => {
53103
const binding = ySyncPluginKey.getState(state).binding
54104
const undoManager = val.undoManager

0 commit comments

Comments
 (0)