import { get } from '@antv/util';
import type { ComboData, EdgeData, GraphData, NodeData } from '../spec';
import type { ElementDatum, ID } from '../types';
/**
* 合并两个 节点/边/Combo 的数据
*
* Merge the data of two nodes/edges/combos
* @param original - 原始数据 | original data
* @param modified - 待合并的数据 | data to be merged
* @returns 合并后的数据 | merged data
* @remarks
* 只会合并第一层的数据,data、style 下的二级数据会被覆盖
*
* Only the first level of data will be merged, the second level of data under data and style will be overwritten
*/
export function mergeElementsData(original: T, modified: Partial): T {
const { data: originalData, style: originalStyle, ...originalAttrs } = original;
const { data: modifiedData, style: modifiedStyle, ...modifiedAttrs } = modified;
const result = {
...originalAttrs,
...modifiedAttrs,
};
if (originalData || modifiedData) {
Object.assign(result, { data: { ...originalData, ...modifiedData } });
}
if (originalStyle || modifiedStyle) {
Object.assign(result, { style: { ...originalStyle, ...modifiedStyle } });
}
return result as T;
}
/**
* 克隆元素数据
*
* Clone clement data
* @param data - 待克隆的数据 | data to be cloned
* @returns 克隆后的数据 | cloned data
* @remarks
* 只会克隆到第二层(data、style)
*
* Only clone to the second level (data, style)
*/
export function cloneElementData(data: T): T {
const { data: customData, style, ...restAttrs } = data;
const clonedData = restAttrs as T;
if (customData) clonedData.data = { ...customData };
if (style) clonedData.style = { ...style };
return clonedData;
}
/**
* 判断数据是否为空
*
* Determine if the data is empty
* @param data - 图数据 | graph data
* @returns 是否为空 | is empty
*/
export function isEmptyData(data: GraphData) {
return !get(data, ['nodes', 'length']) && !get(data, ['edges', 'length']) && !get(data, ['combos', 'length']);
}
/**
* 判断两个元素数据是否相等
*
* Determine if two element data are equal
* @param original - 原始数据 | original data
* @param modified - 修改后的数据 | modified data
* @returns 是否相等 | is equal
* @remarks
* 相比于 isEqual,这个方法不会比较更下层的数据
*
* Compared to isEqual, this method does not compare data at a lower level
*/
export function isElementDataEqual(original: Partial = {}, modified: Partial = {}) {
const {
states: originalStates = [],
data: originalData = {},
style: originalStyle = {},
children: originalChildren = [],
...originalAttrs
} = original;
const {
states: modifiedStates = [],
data: modifiedData = {},
style: modifiedStyle = {},
children: modifiedChildren = [],
...modifiedAttrs
} = modified;
const isArrayEqual = (arr1: unknown[], arr2: unknown[]) => {
if (arr1.length !== arr2.length) return false;
return arr1.every((item, index) => item === arr2[index]);
};
const isObjectEqual = (obj1: Record, obj2: Record) => {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every((key) => obj1[key] === obj2[key]);
};
if (!isObjectEqual(originalAttrs, modifiedAttrs)) return false;
if (!isArrayEqual(originalChildren as ID[], modifiedChildren as ID[])) return false;
if (!isArrayEqual(originalStates, modifiedStates)) return false;
if (!isObjectEqual(originalData, modifiedData)) return false;
if (!isObjectEqual(originalStyle, modifiedStyle)) return false;
return true;
}