65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
|
|
import type { NodeTree, Node, NodeId } from '@craftjs/core';
|
||
|
|
import { getRandomId } from '@craftjs/utils';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns a deep-cloned NodeTree where every node id in the tree
|
||
|
|
* (rootNodeId, the `nodes` map keys, each `node.id`, each internal
|
||
|
|
* `node.data.parent`, every `node.data.nodes` entry, and every
|
||
|
|
* `node.data.linkedNodes` value) has been remapped to a fresh,
|
||
|
|
* collision-free id generated by Craft.js's own id generator.
|
||
|
|
*
|
||
|
|
* The subtree root's `data.parent` is deliberately left untouched --
|
||
|
|
* `actions.addNodeTree(tree, parentId)` sets that itself when the tree is
|
||
|
|
* attached to its new parent.
|
||
|
|
*
|
||
|
|
* The input tree is not mutated.
|
||
|
|
*/
|
||
|
|
export function regenerateTreeIds(tree: NodeTree): NodeTree {
|
||
|
|
const oldIds = Object.keys(tree.nodes);
|
||
|
|
|
||
|
|
// Build old -> new id map first so all cross-references can be rewritten
|
||
|
|
// consistently regardless of iteration order.
|
||
|
|
const idMap = new Map<NodeId, NodeId>();
|
||
|
|
for (const oldId of oldIds) {
|
||
|
|
idMap.set(oldId, getRandomId());
|
||
|
|
}
|
||
|
|
|
||
|
|
const remapId = (id: NodeId): NodeId => idMap.get(id) ?? id;
|
||
|
|
|
||
|
|
const newNodes: Record<NodeId, Node> = {};
|
||
|
|
for (const oldId of oldIds) {
|
||
|
|
const oldNode = tree.nodes[oldId];
|
||
|
|
const newId = idMap.get(oldId)!;
|
||
|
|
|
||
|
|
const newParent =
|
||
|
|
oldId === tree.rootNodeId
|
||
|
|
? oldNode.data.parent // subtree root's parent is set by the caller
|
||
|
|
: oldNode.data.parent !== null && oldNode.data.parent !== undefined
|
||
|
|
? remapId(oldNode.data.parent)
|
||
|
|
: oldNode.data.parent;
|
||
|
|
|
||
|
|
const newLinkedNodes: Record<string, NodeId> = {};
|
||
|
|
for (const [slot, linkedId] of Object.entries(oldNode.data.linkedNodes || {})) {
|
||
|
|
newLinkedNodes[slot] = remapId(linkedId);
|
||
|
|
}
|
||
|
|
|
||
|
|
const newNode: Node = {
|
||
|
|
...oldNode,
|
||
|
|
id: newId,
|
||
|
|
data: {
|
||
|
|
...oldNode.data,
|
||
|
|
parent: newParent,
|
||
|
|
nodes: (oldNode.data.nodes || []).map(remapId),
|
||
|
|
linkedNodes: newLinkedNodes,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
newNodes[newId] = newNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
rootNodeId: remapId(tree.rootNodeId),
|
||
|
|
nodes: newNodes,
|
||
|
|
};
|
||
|
|
}
|