feat: add toHierarchy utility for TreeView, RecursiveList (#2072)

Co-authored-by: Bram <bramhavers@gmail.com>
This commit is contained in:
Eric Liu 2024-12-09 12:22:36 -08:00 committed by GitHub
commit 48afd18e5e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 413 additions and 23 deletions

1
src/TreeView/index.d.ts vendored Normal file
View file

@ -0,0 +1 @@
export { default as TreeView } from "./TreeView.svelte";

View file

@ -152,3 +152,4 @@ export {
HeaderSearch,
} from "./UIShell";
export { UnorderedList } from "./UnorderedList";
export { toHierarchy } from "./utils/toHierarchy";

21
src/utils/toHierarchy.d.ts vendored Normal file
View file

@ -0,0 +1,21 @@
type NodeLike = {
id: string | number;
nodes?: NodeLike[];
[key: string]: any;
};
/** Create a hierarchical tree from a flat array. */
export function toHierarchy<
T extends NodeLike,
K extends keyof Omit<T, "id" | "nodes">,
>(
flatArray: T[] | readonly T[],
/**
* Function that returns the parent ID for a given node.
* @example
* toHierarchy(flatArray, (node) => node.parentId);
*/
getParentId: (node: T) => T[K] | null,
): (T & { nodes?: (T & { nodes?: T[] })[] })[];
export default toHierarchy;

49
src/utils/toHierarchy.js Normal file
View file

@ -0,0 +1,49 @@
// @ts-check
/**
* Create a nested array from a flat array.
* @typedef {Object} NodeLike
* @property {string | number} id - Unique identifier for the node
* @property {NodeLike[]} [nodes] - Optional array of child nodes
* @property {Record<string, any>} [additionalProperties] - Any additional properties
*
* @param {NodeLike[]} flatArray - Array of flat nodes to convert
* @param {function(NodeLike): (string|number|null)} getParentId - Function to get parent ID for a node
* @returns {NodeLike[]} Hierarchical tree structure
*/
export function toHierarchy(flatArray, getParentId) {
/** @type {NodeLike[]} */
const tree = [];
const childrenOf = new Map();
const itemsMap = new Map(flatArray.map((item) => [item.id, item]));
flatArray.forEach((item) => {
const parentId = getParentId(item);
// Only create nodes array if we have children.
const children = childrenOf.get(item.id);
if (children) {
item.nodes = children;
}
// Check if parentId exists using Map instead of array lookup.
const parentExists = parentId && itemsMap.has(parentId);
if (parentId && parentExists) {
if (!childrenOf.has(parentId)) {
childrenOf.set(parentId, []);
}
childrenOf.get(parentId).push(item);
const parent = itemsMap.get(parentId);
if (parent) {
parent.nodes = childrenOf.get(parentId);
}
} else {
tree.push(item);
}
});
return tree;
}
export default toHierarchy;