@obsidize/rx-map
Version:
ES6 Map with rxjs extensions for change detection
80 lines (79 loc) • 3.09 kB
JavaScript
import { transform, isEqual, merge, cloneDeep } from 'lodash';
import { ChangeDetectionResultType } from '../events/change-detection-event';
export function identity(value, ..._args) {
return value;
}
export function isNull(value) {
return typeof value === null;
}
export function isUndefined(value) {
return typeof value === 'undefined';
}
export function isFunction(value) {
return typeof value === 'function';
}
export function isObject(value) {
return typeof value === 'object' && !isNull(value);
}
export function castArray(v) {
return isObject(v) && Array.isArray(v) ? v : [v];
}
export function mergeObjects(a, b) {
return merge(a, b);
}
export function cloneObject(v) {
return cloneDeep(v);
}
// Shamelessly stolen from here:
// https://gist.github.com/Yimiprod/7ee176597fef230d1451#gistcomment-2565071
export function deepDifferenceBetween(current, previous) {
return transform(current, (result, currentValue, key) => {
const previousValue = previous[key];
if (isEqual(currentValue, previousValue))
return;
const recurse = (isObject(currentValue) && isObject(previousValue));
result[key] = recurse ? deepDifferenceBetween(currentValue, previousValue) : currentValue;
});
}
/**
* Evaluates changes between the two given values,
* and constructs a change detection event instance based on the evaluation output.
*
* The output in the "changes" object will consist of all the values that have changed
* when transitioning from 'baseObj' to 'obj', and the plucked values will match the ones in 'obj'.
*/
export function detectChanges(current, previous) {
const withType = (type, changes) => {
const result = { type };
if (changes)
result.changes = changes;
return result;
};
if (isEqual(current, previous))
return withType(ChangeDetectionResultType.NO_CHANGE);
if (current && !previous)
return withType(ChangeDetectionResultType.CREATE);
if (!current && previous)
return withType(ChangeDetectionResultType.DELETE);
return withType(ChangeDetectionResultType.UPDATE, deepDifferenceBetween(current, previous));
}
/**
* Convenience for running change detection on a map state change event and storing the results in the event.
* This is implemented as an optional utility since change detection might not be necessary in some cases,
* and can be quite expensive to run.
*/
export function extractChanges(ev) {
const { value, previousValue } = ev;
const result = detectChanges(value, previousValue);
ev.changes = result.changes;
ev.changeType = result.type;
return ev;
}
/**
* Special variant of change detection that compares against an accumulated state.
*/
export function detectAccumulatedChanges(acc, current) {
const previous = acc ? acc.current : undefined;
const changeDetectionResult = detectChanges(current, previous);
return Object.assign({ current, previous }, changeDetectionResult);
}