Files
site-builder/craft/src/utils/craft-tree.ts
T

65 lines
2.0 KiB
TypeScript
Raw Normal View History

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,
};
}