29 lines
2.4 KiB
TypeScript
29 lines
2.4 KiB
TypeScript
/**
|
|
* ReactFlow适配层的保留标记缓存。为需要保留的节点/边复制一份带__endoleapRetain的投影记录,原对象不变。
|
|
* 标记供应用固定版本的编译扩展使用,不是模型属性,也不应进入history、存储或业务renderer数据。
|
|
*/
|
|
/** Adapter-only flags consumed by the app's pinned React Flow compiler extension.
|
|
* They never enter the document, history, persistence or renderer business data. */
|
|
/** 每个投影使用者创建一份缓存:WeakMap复用同一原对象的带标记副本,previous复用最近一次整个输出。 */
|
|
export function createRetainedProjection(){
|
|
// 弱引用键避免缓存永久占住已被替换的投影对象;只按对象身份缓存,不监测对象内部原地修改。
|
|
const tagged=new WeakMap<object,object>();
|
|
// previous 强引用最近一批输入/输出;WeakMap 不等于整个实例完全不持有投影,空 ids 才清这份整批引用。
|
|
let previous:{nodes:readonly object[];edges:readonly object[];ids:Set<string>;result:{nodes:object[];edges:object[]}}|undefined;
|
|
/** 输入数组和保留ID集合生成节点/边数组;ids为空时直接返回原数组并清除整批缓存。 */
|
|
return <N extends {id:string},E extends {id:string}>(nodes:N[],edges:E[],ids:ReadonlySet<string>):{nodes:N[];edges:E[]}=>{
|
|
// 清 previous 不清 tagged;同一对象日后再次被保留,仍可能复用已有带标记副本。
|
|
if(!ids.size){previous=undefined;return{nodes,edges};}
|
|
// 数组引用及ID成员都相同才复用;快照复制ids,避免调用方之后原地修改Set影响比较基准。
|
|
if(previous?.nodes===nodes&&previous.edges===edges&&previous.ids.size===ids.size&&[...ids].every(id=>previous!.ids.has(id)))return previous.result as {nodes:N[];edges:E[]};
|
|
/** 未命中的记录保留原引用;命中时浅复制顶层加标记,嵌套数据仍与原投影共享。 */
|
|
const retain=<T extends {id:string}>(row:T):T=>{
|
|
if(!ids.has(row.id))return row;
|
|
let value=tagged.get(row);if(!value){value={...row,__endoleapRetain:true};tagged.set(row,value);}return value as T;
|
|
};
|
|
// 非空集合下会映射两张数组,即使没有命中也生成数组;下一次相同输入再复用该result。
|
|
const result={nodes:nodes.map(retain),edges:edges.map(retain)};
|
|
previous={nodes,edges,ids:new Set(ids),result};return result;
|
|
};
|
|
}
|