From d5ffd4fb0a1480078fb9bd727fc3bc6a31030184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20=C3=85hall?= Date: Sun, 9 Feb 2025 09:34:51 +0100 Subject: [PATCH] Tree keyboard navigation --- static/js/notes2.mjs | 156 ++++++++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 55 deletions(-) diff --git a/static/js/notes2.mjs b/static/js/notes2.mjs index 987f2d7..66a328b 100644 --- a/static/js/notes2.mjs +++ b/static/js/notes2.mjs @@ -185,74 +185,27 @@ class Tree extends Component { async keyHandler(event) {//{{{ let handled = true - let nodeExpanded = false - let siblingBefore = null - let siblingExpanded = false - let parent = null const n = this.selectedNode switch (event.key) { case 'j': case 'ArrowDown': - nodeExpanded = this.getNodeExpanded(n.UUID) - - // Last node, not expanded, so it matters not whether it has children or not. - // Traverse upward to nearest parent with next sibling. - if (!nodeExpanded && n.isLastSibling()) { - const wantedNode = this.getParentNodeWithNextSibling(n) - if (wantedNode?.UUID === ROOT_NODE) - break - await _notes2.current.goToNode(wantedNode?.UUID, true, true) - break - } - - if (nodeExpanded && n.isLastSibling() && !n.hasChildren()) { - const wantedNode = this.getParentNodeWithNextSibling(n) - await _notes2.current.goToNode(wantedNode?.UUID, true, true) - break - } - // Node not expanded. Go to this node's next sibling. - // GoToNode will abort if given null. - if (!nodeExpanded || !n.hasChildren()) { - await _notes2.current.goToNode(n.getSiblingAfter()?.UUID, true, true) - break - } - - // Node is expanded. - // Children will be visually beneath this node, if any. - if (nodeExpanded && n.hasChildren()) { - await _notes2.current.goToNode(n.Children[0].UUID, true, true) - break - } + await this.navigateDown(this.selectedNode) break case 'k': case 'ArrowUp': - siblingBefore = n.getSiblingBefore() - if (siblingBefore !== null) - siblingExpanded = this.getNodeExpanded(siblingBefore.UUID) - - if (n.isFirstSibling()) { - parent = n.getParent() - if (parent?.UUID === ROOT_NODE) - break - await _notes2.current.goToNode(parent?.UUID, true, true) - break - } - - if (siblingBefore !== null && siblingExpanded && siblingBefore.hasChildren()) { - await _notes2.current.goToNode(siblingBefore.Children[siblingBefore.Children.length - 1]?.UUID, true, true) - break - } - - if (siblingBefore) { - await _notes2.current.goToNode(siblingBefore.UUID, true, true) - break - } + await this.navigateUp(this.selectedNode) break case 'h': case 'ArrowLeft': + await this.navigateLeft(this.selectedNode) + break + + case 'l': + case 'ArrowRight': + await this.navigateRight(this.selectedNode) break default: @@ -264,6 +217,99 @@ class Tree extends Component { event.stopPropagation() } }//}}} + async navigateLeft(n) {//{{{ + const expanded = this.getNodeExpanded(n.UUID) + if (expanded && n.hasChildren()) { + this.setNodeExpanded(n.UUID, false) + return + } + + if (n.isFirstSibling() && n.getParent().UUID !== ROOT_NODE) { + await _notes2.current.goToNode(n.getParent()?.UUID, true, true) + return + } + + await _notes2.current.goToNode(n.getSiblingBefore()?.UUID, true, true) + }//}}} + async navigateRight(n) {//{{{ + const siblingAfter = n.getSiblingAfter() + const expanded = this.getNodeExpanded(n.UUID) + + if (!expanded && n.hasChildren()) { + this.setNodeExpanded(n.UUID, true) + return + } + + if (expanded && n.hasChildren()) { + await _notes2.current.goToNode(n.Children[0]?.UUID, true, true) + return + } + + if (n.isLastSibling()) { + const nextNode = this.getParentNodeWithNextSibling(n) + await _notes2.current.goToNode(nextNode?.UUID, true, true) + return + } + + await _notes2.current.goToNode(n.getSiblingAfter()?.UUID, true, true) + }//}}} + async navigateUp(n) {//{{{ + let parent = null + const siblingBefore = n.getSiblingBefore() + let siblingExpanded = false + if (siblingBefore !== null) + siblingExpanded = this.getNodeExpanded(siblingBefore.UUID) + + if (n.isFirstSibling()) { + parent = n.getParent() + if (parent?.UUID === ROOT_NODE) + return + await _notes2.current.goToNode(parent?.UUID, true, true) + return + } + + if (siblingBefore !== null && siblingExpanded && siblingBefore.hasChildren()) { + await _notes2.current.goToNode(siblingBefore.Children[siblingBefore.Children.length - 1]?.UUID, true, true) + return + } + + if (siblingBefore) { + await _notes2.current.goToNode(siblingBefore.UUID, true, true) + return + } + }//}}} + async navigateDown(n) {//{{{ + const nodeExpanded = this.getNodeExpanded(n.UUID) + + // Last node, not expanded, so it matters not whether it has children or not. + // Traverse upward to nearest parent with next sibling. + if (!nodeExpanded && n.isLastSibling()) { + const wantedNode = this.getParentNodeWithNextSibling(n) + if (wantedNode?.UUID === ROOT_NODE) + return + await _notes2.current.goToNode(wantedNode?.UUID, true, true) + return + } + + if (nodeExpanded && n.isLastSibling() && !n.hasChildren()) { + const wantedNode = this.getParentNodeWithNextSibling(n) + await _notes2.current.goToNode(wantedNode?.UUID, true, true) + return + } + // Node not expanded. Go to this node's next sibling. + // GoToNode will abort if given null. + if (!nodeExpanded || !n.hasChildren()) { + await _notes2.current.goToNode(n.getSiblingAfter()?.UUID, true, true) + return + } + + // Node is expanded. + // Children will be visually beneath this node, if any. + if (nodeExpanded && n.hasChildren()) { + await _notes2.current.goToNode(n.Children[0].UUID, true, true) + return + } + }//}}} } class TreeNode extends Component {