rich-text-editor
Version:
Rich text editor
80 lines (79 loc) • 3.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = useHistory;
const react_1 = require("react");
function useHistory() {
/** `stack` is an array with all the current history in order.
*
* `pointer` is a number that points to the current position in the history.
* Undo lowers the pointer, redo makes it higher.
*
* Writing anything always clears the history stack above the pointer, adds a new value
* and moves the pointer to the top of the stack.
* */
const [stack, _setStack] = (0, react_1.useState)([]);
const [pointer, _setPointer] = (0, react_1.useState)(0);
/** Refs are needed to make the current value always accessible
* in the handlers these will be ultimately called from. This is a bit convoluted
* and prone to errors if the refs are not used and updated correctly,
* so remember to always use the `set...` wrappers to update the state instead of
* doing it directly.
* */
const stackRef = (0, react_1.useRef)(stack);
const pointerRef = (0, react_1.useRef)(pointer);
const setStack = (newStack) => {
stackRef.current = newStack;
_setStack(newStack);
};
const setPointer = (newPointer) => {
pointerRef.current = newPointer;
_setPointer(newPointer);
};
const canUndo = pointer !== 0;
const canRedo = pointer < stack.length - 1;
const clear = () => {
setStack([{ content: '' }]);
setPointer(0);
};
const write = (value, caretPositionBeforeChange, caretPositionAfterChange) => {
const currentValue = stackRef.current.at(pointerRef.current);
// If the pointer is at a value that's equal to the new value,
// we don't do anything so we don't get duplicate values in the history
if (value === (currentValue === null || currentValue === void 0 ? void 0 : currentValue.content))
return;
const newEntry = {
content: value,
caretPositionBeforeChange: caretPositionBeforeChange,
caretPositionAfterChange: caretPositionAfterChange,
};
const newStack = [...stackRef.current.slice(0, pointerRef.current + 1), newEntry];
setStack(newStack);
setPointer(newStack.length - 1);
};
const undo = () => {
var _a, _b, _c;
const newPointer = pointerRef.current - 1;
if (newPointer < 0)
return undefined;
const caretPosition = (_b = (_a = stackRef.current.at(pointerRef.current)) === null || _a === void 0 ? void 0 : _a.caretPositionBeforeChange) !== null && _b !== void 0 ? _b : 0;
const newValue = stackRef.current.at(newPointer);
setPointer(newPointer);
return { content: (_c = newValue === null || newValue === void 0 ? void 0 : newValue.content) !== null && _c !== void 0 ? _c : '', newCaretPosition: caretPosition };
};
const redo = () => {
const newPointer = pointerRef.current + 1;
if (newPointer >= stackRef.current.length)
return undefined;
const newValue = stackRef.current.at(newPointer);
setPointer(newPointer);
return { content: newValue === null || newValue === void 0 ? void 0 : newValue.content, newCaretPosition: newValue === null || newValue === void 0 ? void 0 : newValue.caretPositionAfterChange };
};
return {
canUndo,
canRedo,
write,
clear,
undo,
redo,
};
}