UNPKG

@e280/quay

Version:

File-browser and outliner UI for the web

69 lines 2.22 kB
import { MapG } from "@e280/stz"; /** * Tree of nestable things. * - Things can have multiple children. * - Things can only have one parent. * - Things can be orphans (bring your own concept of a "root"). */ export class Hierarchy { #children = new MapG(); #parents = new MapG(); has(id) { return this.#children.has(id); } getChildren(id) { return this.#children.require(id); } getParent(id) { return this.#parents.get(id); } /** establish an id as a root with no parents, but ready to accept children */ establishRoot(root) { this.#children.set(root, new Set()); } /** give a parent some children */ attach(parent, ...children) { const siblings = this.getChildren(parent); for (const childId of children) { if (this.getParent(childId)) throw new Error(`child already has parent`); siblings.add(childId); this.#parents.set(childId, parent); this.#children.set(childId, new Set()); } } /** detach this id from its parent in the hierarchy */ detach(id) { if (!this.has(id)) return undefined; const parent = this.getParent(id); if (parent) { const siblings = this.getChildren(parent); siblings.delete(id); this.#parents.delete(id); } } /** destroy all relations associated with this id, and all its descendants */ destroy(id) { const tree = [...this.crawl(id)]; for (const [id] of tree) { this.#children.delete(id); this.#parents.delete(id); } } /** iterate over this id and all its descendants */ *crawl(id, predicate = () => true) { const todo = [[id, []]]; const seen = new Set(); while (todo.length) { const [id, path] = todo.shift(); if (seen.has(id) || !predicate(id, path)) continue; seen.add(id); yield [id, path]; for (const childId of this.getChildren(id)) todo.push([childId, [...path, id]]); } } } //# sourceMappingURL=hierarchy.js.map