UNPKG

marshall-y-slate

Version:
135 lines (113 loc) 3.58 kB
import { Editor, Location, Node, Path, Point, Transforms } from 'slate'; import invariant from 'tiny-invariant'; import * as Y from 'yjs'; import { CustomNode } from '../src/model'; import { YjsEditor } from '../src/plugin/yjs-editor'; export interface TestEditor extends YjsEditor { shouldCaptureYjsUpdates: boolean; capturedYjsUpdates: Uint8Array[]; } export type TransformFunc = (e: Editor) => void; export const TestEditor = { /** * Capture Yjs updates generated by this editor. */ captureYjsUpdate: (e: TestEditor, update: Uint8Array): void => { if (!e.shouldCaptureYjsUpdates) return; e.capturedYjsUpdates.push(update); }, /** * Return captured Yjs updates. */ getCapturedYjsUpdates: (e: TestEditor): Uint8Array[] => { const result = e.capturedYjsUpdates; e.capturedYjsUpdates = []; return result; }, /** * Apply one Yjs update to Yjs. */ applyYjsUpdateToYjs: (e: TestEditor, update: Uint8Array): void => { e.shouldCaptureYjsUpdates = false; invariant(e.sharedType.doc, 'Shared type should be bound to a document'); Y.applyUpdate(e.sharedType.doc, update); e.shouldCaptureYjsUpdates = true; }, /** * Apply multiple Yjs updates to Yjs. */ applyYjsUpdatesToYjs: (e: TestEditor, updates: Uint8Array[]): void => { updates.forEach((update) => { TestEditor.applyYjsUpdateToYjs(e, update); }); }, /** * Apply one TransformFunc to slate. */ applyTransform: (e: TestEditor, transform: TransformFunc): void => { transform(e); }, /** * Apply multiple TransformFuncs to slate. */ applyTransforms: (e: TestEditor, transforms: TransformFunc[]): void => { transforms.forEach((transform) => { TestEditor.applyTransform(e, transform); }); }, makeInsertText: (text: string, at: Location): TransformFunc => { return (e: Editor) => { Transforms.insertText(e, text, { at }); }; }, makeRemoveCharacters: (count: number, at: Location): TransformFunc => { return (e: Editor) => { Transforms.delete(e, { distance: count, at }); }; }, makeInsertNodes: (nodes: (Node | CustomNode) | (Node | CustomNode)[], at: Location): TransformFunc => { return (e: Editor) => { Transforms.insertNodes(e, nodes as Node, { at }); }; }, makeMergeNodes: (at: Path): TransformFunc => { return (e: Editor) => { Transforms.mergeNodes(e, { at }); }; }, makeMoveNodes: (from: Path, to: Path): TransformFunc => { return (e: Editor) => { Transforms.moveNodes(e, { at: from, to }); }; }, makeRemoveNodes: (at: Path): TransformFunc => { return (e: Editor) => { Transforms.removeNodes(e, { at }); }; }, makeSetNodes: (at: Location, props: Partial<CustomNode>): TransformFunc => { return (e: Editor) => { Transforms.setNodes(e, props, { at }); }; }, makeSplitNodes: (at: Location): TransformFunc => { return (e: Editor) => { Transforms.splitNodes(e, { at }); }; }, makeSetSelection: (anchor: Point, focus: Point): TransformFunc => { return (e: Editor) => { Transforms.setSelection(e, { anchor, focus }); }; } }; export function withTest<T extends YjsEditor>(editor: T): T & TestEditor { const e = editor as T & TestEditor; invariant(e.sharedType.doc, 'Shared type should be bound to a document'); e.sharedType.doc.on('update', (updateMessage: Uint8Array) => { TestEditor.captureYjsUpdate(e, updateMessage); }); e.shouldCaptureYjsUpdates = true; e.capturedYjsUpdates = []; return e; }