« get me outta code hell

tui-lib - Pure Node.js library for making visual command-line programs (ala vim, ncdu)
about summary refs log tree commit diff
path: root/ui/primitives/Element.js
diff options
context:
space:
mode:
Diffstat (limited to 'ui/primitives/Element.js')
-rw-r--r--ui/primitives/Element.js80
1 files changed, 80 insertions, 0 deletions
diff --git a/ui/primitives/Element.js b/ui/primitives/Element.js
new file mode 100644
index 0000000..fea8c03
--- /dev/null
+++ b/ui/primitives/Element.js
@@ -0,0 +1,80 @@
+import EventEmitter from 'node:events'
+
+export default class Element extends EventEmitter {
+  // The basic class containing methods for working with an element hierarchy.
+  // Generally speaking, you usually want to extend DisplayElement instead of
+  // this class.
+
+  constructor() {
+    super()
+
+    this.children = []
+    this.parent = null
+  }
+
+  eachDescendant(fn) {
+    // Run a function on this element, all of its children, all of their
+    // children, etc.
+    fn(this)
+    for (const child of this.children) {
+      child.eachDescendant(fn)
+    }
+  }
+
+  addChild(child, afterIndex = this.children.length, {fixLayout = true} = {}) {
+    // TODO Don't let a direct ancestor of this be added as a child. Don't
+    // let itself be one of its childs either!
+
+    if (child === this) {
+      throw exception(
+        'EINVALIDHIERARCHY', 'An element cannot be a child of itself')
+    }
+
+    child.parent = this
+
+    if (afterIndex === this.children.length) {
+      this.children.push(child)
+    } else {
+      this.children.splice(afterIndex, 0, child)
+    }
+
+    if (fixLayout) {
+      child.fixLayout()
+    }
+  }
+
+  removeChild(child, {fixLayout = true} = {}) {
+    // Removes the given child element from the children list of this
+    // element. It won't be rendered in the future. If the given element
+    // isn't a direct child of this element, nothing will happen.
+
+    if (child.parent !== this) {
+      return
+    }
+
+    child.parent = null
+    this.children.splice(this.children.indexOf(child), 1)
+
+    if (fixLayout) {
+      this.fixLayout()
+    }
+  }
+
+  get root() {
+    let el = this
+    while (el.parent) {
+      el = el.parent
+    }
+    return el
+  }
+
+  get directAncestors() {
+    const ancestors = []
+    let el = this
+    while (el.parent) {
+      el = el.parent
+      ancestors.push(el)
+    }
+    return ancestors
+  }
+}