« get me outta code hell

use ESM module syntax & minor cleanups - 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:
author(quasar) nebula <qznebula@protonmail.com>2023-05-12 17:42:09 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-05-13 12:48:36 -0300
commit6ea74c268a12325296a1d2e7fc31b02030ddb8bc (patch)
tree5da94d93acb64e7ab650d240d6cb23c659ad02ca /ui/primitives/Element.js
parente783bcf8522fa68e6b221afd18469c3c265b1bb7 (diff)
use ESM module syntax & minor cleanups
The biggest change here is moving various element classes under
more scope-specific directories, which helps to avoid circular
dependencies and is just cleaner to navigate and expand in the
future.

Otherwise this is a largely uncritical port to ESM module syntax!
There are probably a number of changes and other cleanups that
remain much needed.

Whenever I make changes to tui-lib it's hard to believe it's
already been <INSERT COUNTING NUMBER HERE> years since the
previous time. First commits are from January 2017, and the
code originates a month earlier in KAaRMNoD!
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
+  }
+}