UNPKG

marshall-y-slate

Version:
97 lines (90 loc) 3.18 kB
import { UndoManager } from 'yjs'; import * as Y from 'yjs'; import { YjsEditor } from './yjs-editor'; import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition } from '../cursor/utils'; import { BasePoint, Descendant, Editor, Path, Transforms } from 'slate'; export interface YjsUndoEditor extends YjsEditor { undoManager: UndoManager; undo: () => void; redo: () => void; } export function withUndoManager<T extends YjsEditor>( editor: T, options = { trackedOrigins: [], captureTimeout: 300, deleteFilter: () => true } ): T & YjsUndoEditor { const e = editor as T & YjsUndoEditor; const { onChange } = e; let previousSelection: { anchorRelative: Y.RelativePosition, focusRelative: Y.RelativePosition } | null; const undoManager = new Y.UndoManager(e.sharedType, { ...options, trackedOrigins: new Set([editor].concat(options.trackedOrigins)) }); e.onChange = () => { onChange(); if (!YjsEditor.isRemote(e)) { try { const lastOperation = e.operations[e.operations.length - 1]; if (lastOperation && lastOperation.type === 'set_selection' && lastOperation.newProperties) { const { anchor, focus } = lastOperation.newProperties as any; const anchorRelative = anchor && absolutePositionToRelativePosition(e.sharedType, anchor); const focusRelative = focus && absolutePositionToRelativePosition(e.sharedType, focus); if (anchorRelative && focusRelative) { previousSelection = { anchorRelative, focusRelative }; } } } catch (error) { const e: YjsEditor & { onError: (errorData: { code?: number, name?: string, nativeError?: any, data?: Descendant[] }) => void } = editor as any; if (e.onError) { e.onError({ code: 10004, name: 'get previous relative', nativeError: error }); } } } }; undoManager.on('stack-item-added', (event: any) => { if (event.changedParentTypes.has(e.sharedType) && previousSelection) { event.stackItem.meta.set(e, previousSelection); previousSelection = null; } }); undoManager.on('stack-item-popped', (event: any) => { const selection = event.stackItem.meta.get(e); if (selection) { const anchor = relativePositionToAbsolutePosition(e.sharedType, selection.anchorRelative); const focus = relativePositionToAbsolutePosition(e.sharedType, selection.focusRelative); if (anchor?.path && focus?.path && Editor.hasPath(e, anchor.path as Path) && Editor.hasPath(e, focus.path as Path)) { Transforms.setSelection(e, { anchor: anchor as BasePoint, focus: focus as BasePoint }); } } }); e.undoManager = undoManager; e.undo = () => { YjsEditor.asUndo(e, () => { e.undoManager.undo(); }); Promise.resolve().then(() => Editor.normalize(editor)); }; e.redo = () => { YjsEditor.asUndo(e, () => { e.undoManager.redo(); }); Promise.resolve().then(() => Editor.normalize(editor)); }; return e; }