UNPKG

5.36 kBJavaScriptView Raw
1import { isPlainObject } from 'is-plain-object';
2import { Operation, Editor, Transforms, Path } from 'slate';
3
4// eslint-disable-next-line no-redeclare
5var History = {
6 /**
7 * Check if a value is a `History` object.
8 */
9 isHistory(value) {
10 return isPlainObject(value) && Array.isArray(value.redos) && Array.isArray(value.undos) && (value.redos.length === 0 || Operation.isOperationList(value.redos[0].operations)) && (value.undos.length === 0 || Operation.isOperationList(value.undos[0].operations));
11 }
12};
13
14/**
15 * Weakmaps for attaching state to the editor.
16 */
17var HISTORY = new WeakMap();
18var SAVING = new WeakMap();
19var MERGING = new WeakMap();
20// eslint-disable-next-line no-redeclare
21var HistoryEditor = {
22 /**
23 * Check if a value is a `HistoryEditor` object.
24 */
25 isHistoryEditor(value) {
26 return History.isHistory(value.history) && Editor.isEditor(value);
27 },
28 /**
29 * Get the merge flag's current value.
30 */
31 isMerging(editor) {
32 return MERGING.get(editor);
33 },
34 /**
35 * Get the saving flag's current value.
36 */
37 isSaving(editor) {
38 return SAVING.get(editor);
39 },
40 /**
41 * Redo to the previous saved state.
42 */
43 redo(editor) {
44 editor.redo();
45 },
46 /**
47 * Undo to the previous saved state.
48 */
49 undo(editor) {
50 editor.undo();
51 },
52 /**
53 * Apply a series of changes inside a synchronous `fn`, without merging any of
54 * the new operations into previous save point in the history.
55 */
56 withoutMerging(editor, fn) {
57 var prev = HistoryEditor.isMerging(editor);
58 MERGING.set(editor, false);
59 fn();
60 MERGING.set(editor, prev);
61 },
62 /**
63 * Apply a series of changes inside a synchronous `fn`, without saving any of
64 * their operations into the history.
65 */
66 withoutSaving(editor, fn) {
67 var prev = HistoryEditor.isSaving(editor);
68 SAVING.set(editor, false);
69 fn();
70 SAVING.set(editor, prev);
71 }
72};
73
74/**
75 * The `withHistory` plugin keeps track of the operation history of a Slate
76 * editor as operations are applied to it, using undo and redo stacks.
77 *
78 * If you are using TypeScript, you must extend Slate's CustomTypes to use
79 * this plugin.
80 *
81 * See https://docs.slatejs.org/concepts/11-typescript to learn how.
82 */
83var withHistory = editor => {
84 var e = editor;
85 var {
86 apply
87 } = e;
88 e.history = {
89 undos: [],
90 redos: []
91 };
92 e.redo = () => {
93 var {
94 history
95 } = e;
96 var {
97 redos
98 } = history;
99 if (redos.length > 0) {
100 var batch = redos[redos.length - 1];
101 if (batch.selectionBefore) {
102 Transforms.setSelection(e, batch.selectionBefore);
103 }
104 HistoryEditor.withoutSaving(e, () => {
105 Editor.withoutNormalizing(e, () => {
106 for (var op of batch.operations) {
107 e.apply(op);
108 }
109 });
110 });
111 history.redos.pop();
112 e.writeHistory('undos', batch);
113 }
114 };
115 e.undo = () => {
116 var {
117 history
118 } = e;
119 var {
120 undos
121 } = history;
122 if (undos.length > 0) {
123 var batch = undos[undos.length - 1];
124 HistoryEditor.withoutSaving(e, () => {
125 Editor.withoutNormalizing(e, () => {
126 var inverseOps = batch.operations.map(Operation.inverse).reverse();
127 for (var op of inverseOps) {
128 e.apply(op);
129 }
130 if (batch.selectionBefore) {
131 Transforms.setSelection(e, batch.selectionBefore);
132 }
133 });
134 });
135 e.writeHistory('redos', batch);
136 history.undos.pop();
137 }
138 };
139 e.apply = op => {
140 var {
141 operations,
142 history
143 } = e;
144 var {
145 undos
146 } = history;
147 var lastBatch = undos[undos.length - 1];
148 var lastOp = lastBatch && lastBatch.operations[lastBatch.operations.length - 1];
149 var save = HistoryEditor.isSaving(e);
150 var merge = HistoryEditor.isMerging(e);
151 if (save == null) {
152 save = shouldSave(op);
153 }
154 if (save) {
155 if (merge == null) {
156 if (lastBatch == null) {
157 merge = false;
158 } else if (operations.length !== 0) {
159 merge = true;
160 } else {
161 merge = shouldMerge(op, lastOp);
162 }
163 }
164 if (lastBatch && merge) {
165 lastBatch.operations.push(op);
166 } else {
167 var batch = {
168 operations: [op],
169 selectionBefore: e.selection
170 };
171 e.writeHistory('undos', batch);
172 }
173 while (undos.length > 100) {
174 undos.shift();
175 }
176 history.redos = [];
177 }
178 apply(op);
179 };
180 e.writeHistory = (stack, batch) => {
181 e.history[stack].push(batch);
182 };
183 return e;
184};
185/**
186 * Check whether to merge an operation into the previous operation.
187 */
188var shouldMerge = (op, prev) => {
189 if (prev && op.type === 'insert_text' && prev.type === 'insert_text' && op.offset === prev.offset + prev.text.length && Path.equals(op.path, prev.path)) {
190 return true;
191 }
192 if (prev && op.type === 'remove_text' && prev.type === 'remove_text' && op.offset + op.text.length === prev.offset && Path.equals(op.path, prev.path)) {
193 return true;
194 }
195 return false;
196};
197/**
198 * Check whether an operation needs to be saved to the history.
199 */
200var shouldSave = (op, prev) => {
201 if (op.type === 'set_selection') {
202 return false;
203 }
204 return true;
205};
206
207export { HISTORY, History, HistoryEditor, MERGING, SAVING, withHistory };
208//# sourceMappingURL=index.es.js.map