jotai-history
Version:
95 lines • 3.32 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.withUndo = withUndo;
const vanilla_1 = require("jotai/vanilla");
const actions_1 = require("./actions.js");
/**
* @param targetAtom a primitive atom or equivalent
* @param limit the maximum number of history states to keep
* @returns an atom with undo/redo capabilities
*/
function withUndo(historyAtom, targetAtom, limit, getArgs) {
const createRef = () => ({
index: 0,
stack: [],
action: null,
});
const refreshAtom = (0, vanilla_1.atom)(0);
const refAtom = (0, vanilla_1.atom)(createRef, (get, set, action) => {
if (action === actions_1.RESET) {
void Object.assign(get(refAtom), createRef());
set(refreshAtom, (v) => v + 1);
}
});
refAtom.onMount = (setAtom) => () => setAtom(actions_1.RESET);
refAtom.debugPrivate = true;
const updateRefAtom = (0, vanilla_1.atom)((get) => {
const history = get(historyAtom);
const ref = get(refAtom);
get(refreshAtom);
if (ref.action) {
// recalculation caused by undo/redo/reset
ref.action = null;
}
else {
// Remove future states if any
ref.stack = ref.stack.slice(0, ref.index + 1);
// Push the current state to the stack
ref.stack.push(history[0]);
// Limit the stack
ref.stack = ref.stack.slice(-limit);
// Move the current index to the end
ref.index = ref.stack.length - 1;
}
return Object.assign({}, ref);
}, (get, set) => {
const ref = get(refAtom);
ref.stack = [get(targetAtom)];
return () => set(refAtom, actions_1.RESET);
});
updateRefAtom.onMount = (mount) => mount();
updateRefAtom.debugPrivate = true;
const canUndoAtom = (0, vanilla_1.atom)((get) => {
return get(updateRefAtom).index > 0;
});
const canRedoAtom = (0, vanilla_1.atom)((get) => {
const ref = get(updateRefAtom);
return ref.index < ref.stack.length - 1;
});
return (0, vanilla_1.atom)((get) => ({
canUndo: get(canUndoAtom),
canRedo: get(canRedoAtom),
}), (get, set, action) => {
const ref = get(refAtom);
const setCurrentState = (index) => {
if (index in ref.stack) {
const value = ref.stack[index];
const args = typeof getArgs === 'function' ? getArgs(value) : [value];
set(targetAtom, ...args);
}
};
if (action === actions_1.UNDO) {
if (get(canUndoAtom)) {
ref.action = actions_1.UNDO;
get(historyAtom).shift();
setCurrentState(--ref.index);
get(historyAtom).shift();
}
}
else if (action === actions_1.REDO) {
if (get(canRedoAtom)) {
ref.action = actions_1.REDO;
setCurrentState(++ref.index);
}
}
else if (action === actions_1.RESET) {
ref.action = actions_1.RESET;
set(refAtom, action);
}
else {
return;
}
set(refreshAtom, (v) => v + 1);
});
}
//# sourceMappingURL=withUndo.js.map