You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
5.4 KiB
258 lines
5.4 KiB
import { isPlainObject } from 'is-plain-object';
|
|
import { Operation, Editor, Path } from 'slate';
|
|
|
|
var History = {
|
|
/**
|
|
* Check if a value is a `History` object.
|
|
*/
|
|
isHistory(value) {
|
|
return isPlainObject(value) && Array.isArray(value.redos) && Array.isArray(value.undos) && (value.redos.length === 0 || Operation.isOperationList(value.redos[0])) && (value.undos.length === 0 || Operation.isOperationList(value.undos[0]));
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Weakmaps for attaching state to the editor.
|
|
*/
|
|
|
|
var HISTORY = new WeakMap();
|
|
var SAVING = new WeakMap();
|
|
var MERGING = new WeakMap();
|
|
var HistoryEditor = {
|
|
/**
|
|
* Check if a value is a `HistoryEditor` object.
|
|
*/
|
|
isHistoryEditor(value) {
|
|
return History.isHistory(value.history) && Editor.isEditor(value);
|
|
},
|
|
|
|
/**
|
|
* Get the merge flag's current value.
|
|
*/
|
|
isMerging(editor) {
|
|
return MERGING.get(editor);
|
|
},
|
|
|
|
/**
|
|
* Get the saving flag's current value.
|
|
*/
|
|
isSaving(editor) {
|
|
return SAVING.get(editor);
|
|
},
|
|
|
|
/**
|
|
* Redo to the previous saved state.
|
|
*/
|
|
redo(editor) {
|
|
editor.redo();
|
|
},
|
|
|
|
/**
|
|
* Undo to the previous saved state.
|
|
*/
|
|
undo(editor) {
|
|
editor.undo();
|
|
},
|
|
|
|
/**
|
|
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
* the new operations into previous save point in the history.
|
|
*/
|
|
withoutMerging(editor, fn) {
|
|
var prev = HistoryEditor.isMerging(editor);
|
|
MERGING.set(editor, false);
|
|
fn();
|
|
MERGING.set(editor, prev);
|
|
},
|
|
|
|
/**
|
|
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
* their operations into the history.
|
|
*/
|
|
withoutSaving(editor, fn) {
|
|
var prev = HistoryEditor.isSaving(editor);
|
|
SAVING.set(editor, false);
|
|
fn();
|
|
SAVING.set(editor, prev);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* The `withHistory` plugin keeps track of the operation history of a Slate
|
|
* editor as operations are applied to it, using undo and redo stacks.
|
|
*
|
|
* If you are using TypeScript, you must extend Slate's CustomTypes to use
|
|
* this plugin.
|
|
*
|
|
* See https://docs.slatejs.org/concepts/11-typescript to learn how.
|
|
*/
|
|
|
|
var withHistory = editor => {
|
|
var e = editor;
|
|
var {
|
|
apply
|
|
} = e;
|
|
e.history = {
|
|
undos: [],
|
|
redos: []
|
|
};
|
|
|
|
e.redo = () => {
|
|
var {
|
|
history
|
|
} = e;
|
|
var {
|
|
redos
|
|
} = history;
|
|
|
|
if (redos.length > 0) {
|
|
var batch = redos[redos.length - 1];
|
|
HistoryEditor.withoutSaving(e, () => {
|
|
Editor.withoutNormalizing(e, () => {
|
|
for (var op of batch) {
|
|
e.apply(op);
|
|
}
|
|
});
|
|
});
|
|
history.redos.pop();
|
|
history.undos.push(batch);
|
|
}
|
|
};
|
|
|
|
e.undo = () => {
|
|
var {
|
|
history
|
|
} = e;
|
|
var {
|
|
undos
|
|
} = history;
|
|
|
|
if (undos.length > 0) {
|
|
var batch = undos[undos.length - 1];
|
|
HistoryEditor.withoutSaving(e, () => {
|
|
Editor.withoutNormalizing(e, () => {
|
|
var inverseOps = batch.map(Operation.inverse).reverse();
|
|
|
|
for (var op of inverseOps) {
|
|
e.apply(op);
|
|
}
|
|
});
|
|
});
|
|
history.redos.push(batch);
|
|
history.undos.pop();
|
|
}
|
|
};
|
|
|
|
e.apply = op => {
|
|
var {
|
|
operations,
|
|
history
|
|
} = e;
|
|
var {
|
|
undos
|
|
} = history;
|
|
var lastBatch = undos[undos.length - 1];
|
|
var lastOp = lastBatch && lastBatch[lastBatch.length - 1];
|
|
var overwrite = shouldOverwrite(op, lastOp);
|
|
var save = HistoryEditor.isSaving(e);
|
|
var merge = HistoryEditor.isMerging(e);
|
|
|
|
if (save == null) {
|
|
save = shouldSave(op);
|
|
}
|
|
|
|
if (save) {
|
|
if (merge == null) {
|
|
if (lastBatch == null) {
|
|
merge = false;
|
|
} else if (operations.length !== 0) {
|
|
merge = true;
|
|
} else {
|
|
merge = shouldMerge(op, lastOp) || overwrite;
|
|
}
|
|
}
|
|
|
|
if (lastBatch && merge) {
|
|
if (overwrite) {
|
|
lastBatch.pop();
|
|
}
|
|
|
|
lastBatch.push(op);
|
|
} else {
|
|
var batch = [op];
|
|
undos.push(batch);
|
|
}
|
|
|
|
while (undos.length > 100) {
|
|
undos.shift();
|
|
}
|
|
|
|
if (shouldClear(op)) {
|
|
history.redos = [];
|
|
}
|
|
}
|
|
|
|
apply(op);
|
|
};
|
|
|
|
return e;
|
|
};
|
|
/**
|
|
* Check whether to merge an operation into the previous operation.
|
|
*/
|
|
|
|
var shouldMerge = (op, prev) => {
|
|
if (op.type === 'set_selection') {
|
|
return true;
|
|
}
|
|
|
|
if (prev && op.type === 'insert_text' && prev.type === 'insert_text' && op.offset === prev.offset + prev.text.length && Path.equals(op.path, prev.path)) {
|
|
return true;
|
|
}
|
|
|
|
if (prev && op.type === 'remove_text' && prev.type === 'remove_text' && op.offset + op.text.length === prev.offset && Path.equals(op.path, prev.path)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
/**
|
|
* Check whether an operation needs to be saved to the history.
|
|
*/
|
|
|
|
|
|
var shouldSave = (op, prev) => {
|
|
if (op.type === 'set_selection' && (op.properties == null || op.newProperties == null)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/**
|
|
* Check whether an operation should overwrite the previous one.
|
|
*/
|
|
|
|
|
|
var shouldOverwrite = (op, prev) => {
|
|
if (prev && op.type === 'set_selection' && prev.type === 'set_selection') {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
/**
|
|
* Check whether an operation should clear the redos stack.
|
|
*/
|
|
|
|
|
|
var shouldClear = op => {
|
|
if (op.type === 'set_selection') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
export { HISTORY, History, HistoryEditor, MERGING, SAVING, withHistory };
|
|
//# sourceMappingURL=index.es.js.map
|
|
|