patched-undo-peasy
Advanced tools
Comparing version 0.1.6 to 0.1.7
{ | ||
"name": "patched-undo-peasy", | ||
"version": "0.1.6", | ||
"version": "0.1.7", | ||
"main": "src/UndoRedoApi.ts", | ||
@@ -5,0 +5,0 @@ "dependencies": { |
import "chai/register-should"; | ||
import _ from "lodash"; | ||
import { findGetters, removeDeep } from "../Utils"; | ||
import { findGetters, copyFiltered } from "../Utils"; | ||
@@ -33,5 +33,5 @@ | ||
const filtered = removeDeep(withFun, _.isFunction); | ||
const filtered = copyFiltered(withFun, _.isFunction); | ||
filtered.should.deep.equal(src); | ||
filtered.should.not.equal(src); | ||
}); |
import _ from "lodash"; | ||
import { Action, action } from "patched-peasy"; | ||
import { KeyPathFilter } from "./UndoRedoMiddleware"; | ||
import { AnyObject, findGetters, removeDeep } from "./Utils"; | ||
import { AnyObject, copyFiltered, findGetters } from "./Utils"; | ||
/** | ||
* WithUndo defines actions and history state to support Undo/Redo. | ||
* | ||
* | ||
* The root application model interface should extend WithUndo. | ||
@@ -20,3 +20,3 @@ */ | ||
* undoable adds state and action fields to a model instance support undo. | ||
* | ||
* | ||
* The root application model should be wrapped in undoable(). | ||
@@ -26,3 +26,10 @@ * @param model application model | ||
export function undoable<M extends {}>(model: M): ModelWithUndo<M> { | ||
return { ...model, undoHistory: undoModel, undoSave, undoUndo, undoRedo, undoReset }; | ||
return { | ||
...model, | ||
undoHistory: undoModel, | ||
undoSave, | ||
undoUndo, | ||
undoRedo, | ||
undoReset, | ||
}; | ||
} | ||
@@ -42,3 +49,2 @@ | ||
const undoModel: UndoHistory = { undo: [], redo: [] }; | ||
@@ -77,12 +83,17 @@ | ||
// remove keys that shouldn't be saved in undo history (computeds, user filtered, and history state) | ||
const filteredCopy: AnyObject = removeDeep(draftState, (_value, key, path) => { | ||
const fullPath = path.concat([key]); | ||
const isComputed = !!computeds.find((computedPath) => | ||
_.isEqual(fullPath, computedPath) | ||
); | ||
return isComputed || params.noSaveKeys(key, path); | ||
}); | ||
delete filteredCopy["undoHistory"]; | ||
draftState.undoHistory.current = filteredCopy; | ||
const filteredState: AnyObject = copyFiltered( | ||
draftState, | ||
(_value, key, path) => { | ||
if (path.length === 0 && key === "undoHistory") { | ||
return true; | ||
} | ||
const fullPath = path.concat([key]); | ||
const isComputed = !!computeds.find((computedPath) => | ||
_.isEqual(fullPath, computedPath) | ||
); | ||
return isComputed || params.noSaveKeys(key, path); | ||
} | ||
); | ||
draftState.undoHistory.current = filteredState; | ||
} | ||
@@ -122,2 +133,1 @@ | ||
}); | ||
@@ -5,12 +5,15 @@ import _ from "lodash"; | ||
/** return a copy of an object with fields removed by a filter function */ | ||
export function removeDeep( | ||
/** Return a copy of an object with some fields elided. | ||
* | ||
* @param fn copy will not include source properties for which this function returns true | ||
*/ | ||
export function copyFiltered( | ||
src: ObjOrArray, | ||
fn: (value: any, key: string, path: string[]) => boolean | ||
): ObjOrArray { | ||
return removeRecursive(src, []); | ||
return filterCopyRecurse(src, []); | ||
function removeRecursive(obj: ObjOrArray, path: string[]): ObjOrArray { | ||
function filterCopyRecurse(obj: ObjOrArray, path: string[]): ObjOrArray { | ||
if (_.isArray(obj)) { | ||
return obj.map((elem) => removeDeep(elem, fn)); | ||
return obj.map((elem) => filterCopyRecurse(elem, path)); | ||
} | ||
@@ -24,3 +27,3 @@ | ||
key, | ||
removeRecursive(value, path.concat([key])), | ||
filterCopyRecurse(value, path.concat([key])), | ||
]); | ||
@@ -35,3 +38,6 @@ return Object.fromEntries(copies); | ||
/** replace undefined fields with a default value */ | ||
export function replaceUndefined<T extends Partial<U>, U>(obj: T, defaults: U): T & U { | ||
export function replaceUndefined<T extends Partial<U>, U>( | ||
obj: T, | ||
defaults: U | ||
): T & U { | ||
const result = { ...defaults, ...removeUndefined(obj) }; | ||
@@ -56,3 +62,7 @@ return result; | ||
export function findGetters(src: AnyObject, pathPrefix: string[] = []): string[][] { | ||
/** @return the paths of to all getter properties nested in a source object */ | ||
export function findGetters( | ||
src: AnyObject, | ||
pathPrefix: string[] = [] | ||
): string[][] { | ||
const result = Object.entries(src).flatMap(([key, value]) => { | ||
@@ -72,3 +82,2 @@ if (isGetter(src, key)) { | ||
function isGetter(src: {}, key: string): boolean { | ||
@@ -80,2 +89,2 @@ const desc = Object.getOwnPropertyDescriptor(src, key); | ||
return false; | ||
} | ||
} |
18874
492