238 lines
7.9 KiB
JavaScript
238 lines
7.9 KiB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
};
|
|
import { ObjectExt, Basecoat } from '@antv/x6-common';
|
|
export class Store extends Basecoat {
|
|
constructor(data = {}) {
|
|
super();
|
|
this.pending = false;
|
|
this.changing = false;
|
|
this.data = {};
|
|
this.mutate(ObjectExt.cloneDeep(data));
|
|
this.changed = {};
|
|
}
|
|
mutate(data, options = {}) {
|
|
const unset = options.unset === true;
|
|
const silent = options.silent === true;
|
|
const changes = [];
|
|
const changing = this.changing;
|
|
this.changing = true;
|
|
if (!changing) {
|
|
this.previous = ObjectExt.cloneDeep(this.data);
|
|
this.changed = {};
|
|
}
|
|
const current = this.data;
|
|
const previous = this.previous;
|
|
const changed = this.changed;
|
|
Object.keys(data).forEach((k) => {
|
|
const key = k;
|
|
const newValue = data[key];
|
|
if (!ObjectExt.isEqual(current[key], newValue)) {
|
|
changes.push(key);
|
|
}
|
|
if (!ObjectExt.isEqual(previous[key], newValue)) {
|
|
changed[key] = newValue;
|
|
}
|
|
else {
|
|
delete changed[key];
|
|
}
|
|
if (unset) {
|
|
delete current[key];
|
|
}
|
|
else {
|
|
current[key] = newValue;
|
|
}
|
|
});
|
|
if (!silent && changes.length > 0) {
|
|
this.pending = true;
|
|
this.pendingOptions = options;
|
|
changes.forEach((key) => {
|
|
this.emit('change:*', {
|
|
key,
|
|
options,
|
|
store: this,
|
|
current: current[key],
|
|
previous: previous[key],
|
|
});
|
|
});
|
|
}
|
|
if (changing) {
|
|
return this;
|
|
}
|
|
if (!silent) {
|
|
// Changes can be recursively nested within `"change"` events.
|
|
while (this.pending) {
|
|
this.pending = false;
|
|
this.emit('changed', {
|
|
current,
|
|
previous,
|
|
store: this,
|
|
options: this.pendingOptions,
|
|
});
|
|
}
|
|
}
|
|
this.pending = false;
|
|
this.changing = false;
|
|
this.pendingOptions = null;
|
|
return this;
|
|
}
|
|
get(key, defaultValue) {
|
|
if (key == null) {
|
|
return this.data;
|
|
}
|
|
const ret = this.data[key];
|
|
return ret == null ? defaultValue : ret;
|
|
}
|
|
getPrevious(key) {
|
|
if (this.previous) {
|
|
const ret = this.previous[key];
|
|
return ret == null ? undefined : ret;
|
|
}
|
|
return undefined;
|
|
}
|
|
set(key, value, options) {
|
|
if (key != null) {
|
|
if (typeof key === 'object') {
|
|
this.mutate(key, value);
|
|
}
|
|
else {
|
|
this.mutate({ [key]: value }, options);
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
remove(key, options) {
|
|
const empty = undefined;
|
|
const subset = {};
|
|
let opts;
|
|
if (typeof key === 'string') {
|
|
subset[key] = empty;
|
|
opts = options;
|
|
}
|
|
else if (Array.isArray(key)) {
|
|
key.forEach((k) => (subset[k] = empty));
|
|
opts = options;
|
|
}
|
|
else {
|
|
// eslint-disable-next-line
|
|
for (const key in this.data) {
|
|
subset[key] = empty;
|
|
}
|
|
opts = key;
|
|
}
|
|
this.mutate(subset, Object.assign(Object.assign({}, opts), { unset: true }));
|
|
return this;
|
|
}
|
|
getByPath(path) {
|
|
return ObjectExt.getByPath(this.data, path, '/');
|
|
}
|
|
setByPath(path, value, options = {}) {
|
|
const delim = '/';
|
|
const pathArray = Array.isArray(path) ? [...path] : path.split(delim);
|
|
const pathString = Array.isArray(path) ? path.join(delim) : path;
|
|
const property = pathArray[0];
|
|
const pathArrayLength = pathArray.length;
|
|
options.propertyPath = pathString;
|
|
options.propertyValue = value;
|
|
options.propertyPathArray = pathArray;
|
|
if (pathArrayLength === 1) {
|
|
this.set(property, value, options);
|
|
}
|
|
else {
|
|
const update = {};
|
|
let diver = update;
|
|
let nextKey = property;
|
|
// Initialize the nested object. Subobjects are either arrays or objects.
|
|
// An empty array is created if the sub-key is an integer. Otherwise, an
|
|
// empty object is created.
|
|
for (let i = 1; i < pathArrayLength; i += 1) {
|
|
const key = pathArray[i];
|
|
const isArrayIndex = Number.isFinite(Number(key));
|
|
diver = diver[nextKey] = isArrayIndex ? [] : {};
|
|
nextKey = key;
|
|
}
|
|
// Fills update with the `value` on `path`.
|
|
ObjectExt.setByPath(update, pathArray, value, delim);
|
|
const data = ObjectExt.cloneDeep(this.data);
|
|
// If rewrite mode enabled, we replace value referenced by path with the
|
|
// new one (we don't merge).
|
|
if (options.rewrite) {
|
|
ObjectExt.unsetByPath(data, path, delim);
|
|
}
|
|
const merged = ObjectExt.merge(data, update);
|
|
this.set(property, merged[property], options);
|
|
}
|
|
return this;
|
|
}
|
|
removeByPath(path, options) {
|
|
const keys = Array.isArray(path) ? path : path.split('/');
|
|
const key = keys[0];
|
|
if (keys.length === 1) {
|
|
this.remove(key, options);
|
|
}
|
|
else {
|
|
const paths = keys.slice(1);
|
|
const prop = ObjectExt.cloneDeep(this.get(key));
|
|
if (prop) {
|
|
ObjectExt.unsetByPath(prop, paths);
|
|
}
|
|
this.set(key, prop, options);
|
|
}
|
|
return this;
|
|
}
|
|
hasChanged(key) {
|
|
if (key == null) {
|
|
return Object.keys(this.changed).length > 0;
|
|
}
|
|
return key in this.changed;
|
|
}
|
|
/**
|
|
* Returns an object containing all the data that have changed,
|
|
* or `null` if there are no changes. Useful for determining what
|
|
* parts of a view need to be updated.
|
|
*/
|
|
getChanges(diff) {
|
|
if (diff == null) {
|
|
return this.hasChanged() ? ObjectExt.cloneDeep(this.changed) : null;
|
|
}
|
|
const old = this.changing ? this.previous : this.data;
|
|
const changed = {};
|
|
let hasChanged;
|
|
// eslint-disable-next-line
|
|
for (const key in diff) {
|
|
const val = diff[key];
|
|
if (!ObjectExt.isEqual(old[key], val)) {
|
|
changed[key] = val;
|
|
hasChanged = true;
|
|
}
|
|
}
|
|
return hasChanged ? ObjectExt.cloneDeep(changed) : null;
|
|
}
|
|
/**
|
|
* Returns a copy of the store's `data` object.
|
|
*/
|
|
toJSON() {
|
|
return ObjectExt.cloneDeep(this.data);
|
|
}
|
|
clone() {
|
|
const constructor = this.constructor;
|
|
return new constructor(this.data);
|
|
}
|
|
dispose() {
|
|
this.off();
|
|
this.data = {};
|
|
this.previous = {};
|
|
this.changed = {};
|
|
this.pending = false;
|
|
this.changing = false;
|
|
this.pendingOptions = null;
|
|
this.trigger('disposed', { store: this });
|
|
}
|
|
}
|
|
__decorate([
|
|
Basecoat.dispose()
|
|
], Store.prototype, "dispose", null);
|
|
//# sourceMappingURL=store.js.map
|