From cc9ff1c6b2ff79b4041aed641a859f3e5d7189c6 Mon Sep 17 00:00:00 2001 From: Paritosh Maurya Date: Sun, 4 May 2025 11:37:41 +0530 Subject: [PATCH 1/5] Added support for keyboard on component tab --- .../applet/src/modules/components/index.vue | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/packages/applet/src/modules/components/index.vue b/packages/applet/src/modules/components/index.vue index 929245e5..d6af8d2c 100644 --- a/packages/applet/src/modules/components/index.vue +++ b/packages/applet/src/modules/components/index.vue @@ -279,6 +279,99 @@ useEventListener('keydown', (event) => { } }) +useEventListener('keydown', (event) => { + if (!activeComponentId.value) + return + + switch (event.key) { + case 'ArrowRight': + handleArrowRight() + break + case 'ArrowLeft': + handleArrowLeft() + break + case 'ArrowDown': + handleArrowDown() + event.preventDefault() + break + case 'ArrowUp': + handleArrowUp() + event.preventDefault() + break + case ' ': + case 'Enter': { + handleEnter() + event.preventDefault() + break + } + } +}) + +function handleArrowRight() { + if (!expandedTreeNodes.value.includes(activeComponentId.value)) { + expandedTreeNodes.value.push(activeComponentId.value) + } +} + +function handleArrowLeft() { + if (expandedTreeNodes.value.includes(activeComponentId.value)) { + expandedTreeNodes.value.splice(expandedTreeNodes.value.indexOf(activeComponentId.value), 1) + } +} + +function handleArrowDown() { + const activeComponentIdIndex = flattenedTreeNodesIds.value.indexOf(activeComponentId.value) + const isActiveComponentExpanded = expandedTreeNodes.value.includes(activeComponentId.value) + + if (isActiveComponentExpanded && activeComponentIdIndex >= 0 && activeComponentIdIndex < flattenedTreeNodesIds.value.length - 1) { + activeComponentId.value = flattenedTreeNodesIds.value[activeComponentIdIndex + 1] + } + else if (activeComponentIdIndex === 0) { + return false + } + else { + const listIndex = treeNodeLinkedList.value.findIndex(arr => arr.includes(activeComponentId.value)) + const nextTree = treeNodeLinkedList.value[listIndex + 1] + if (nextTree) { + activeComponentId.value = nextTree[1] + } + } +} + +function handleArrowUp() { + const isSubTreeRoot = treeNodeLinkedList.value.some(chain => chain[1] === activeComponentId.value) + + if (isSubTreeRoot) { + activeComponentId.value = getPrevExpandedNode() + } + else { + const currentIndex = flattenedTreeNodesIds.value.indexOf(activeComponentId.value) + if (currentIndex > 0) { + activeComponentId.value = flattenedTreeNodesIds.value[currentIndex - 1] + } + } +} + +function handleEnter() { + const node = flattenedTreeNodes.value.find(item => item.id === activeComponentId.value) + if (!node?.children?.length) + return + + const index = expandedTreeNodes.value.indexOf(activeComponentId.value) + if (index === -1) + expandedTreeNodes.value.push(activeComponentId.value) + else expandedTreeNodes.value.splice(index, 1) +} + +function getPrevExpandedNode() { + const list = treeNodeLinkedList.value + const listIndex = list.findIndex((chain: string[]) => chain.includes(activeComponentId.value)) + if (listIndex > 0) { + return list[listIndex - 1].find(id => !expandedTreeNodes.value.includes(id)) || list[listIndex - 1][1] + } + return list[listIndex][0] +} + function scrollToComponent() { rpc.value.scrollToComponent(activeComponentId.value) } From 48db6b0fe9749499e4a0f8668e22be3cfbf0a94f Mon Sep 17 00:00:00 2001 From: mauryapari Date: Mon, 5 May 2025 19:27:10 +0530 Subject: [PATCH 2/5] Updated code for moving up and down the component tree --- .../applet/src/modules/components/index.vue | 130 ++++++++++++------ 1 file changed, 87 insertions(+), 43 deletions(-) diff --git a/packages/applet/src/modules/components/index.vue b/packages/applet/src/modules/components/index.vue index d6af8d2c..60ee13b3 100644 --- a/packages/applet/src/modules/components/index.vue +++ b/packages/applet/src/modules/components/index.vue @@ -11,7 +11,7 @@ import { vTooltip, VueButton, VueDialog, VueInput } from '@vue/devtools-ui' import { useElementSize, useEventListener, useToggle, watchDebounced } from '@vueuse/core' import { flatten, groupBy } from 'lodash-es' import { Pane, Splitpanes } from 'splitpanes' -import { computed, onUnmounted, ref, watch, watchEffect } from 'vue' +import { computed, nextTick, onUnmounted, ref, watch, watchEffect } from 'vue' import SelectiveList from '~/components/basic/SelectiveList.vue' import RootStateViewer from '~/components/state/RootStateViewer.vue' import ComponentTree from '~/components/tree/TreeViewer.vue' @@ -283,32 +283,40 @@ useEventListener('keydown', (event) => { if (!activeComponentId.value) return - switch (event.key) { - case 'ArrowRight': - handleArrowRight() - break - case 'ArrowLeft': - handleArrowLeft() - break - case 'ArrowDown': - handleArrowDown() - event.preventDefault() - break - case 'ArrowUp': - handleArrowUp() - event.preventDefault() - break - case ' ': - case 'Enter': { - handleEnter() - event.preventDefault() - break + nextTick(() => { + switch (event.key) { + case 'ArrowRight':{ + handleArrowRight() + break + } + case 'ArrowLeft': { + handleArrowLeft() + break + } + case 'ArrowDown': { + handleArrowDown() + event.preventDefault() + return false + } + case 'ArrowUp': { + handleArrowUp() + event.preventDefault() + break + } + case ' ': + case 'Enter': { + handleEnter() + break + } } - } + }) }) function handleArrowRight() { - if (!expandedTreeNodes.value.includes(activeComponentId.value)) { + const isPresentInExpandedNodes = expandedTreeNodes.value.includes(activeComponentId.value) + const hasChildren = flattenedTreeNodes.value.find(item => item.id === activeComponentId.value)?.children?.length + + if (!isPresentInExpandedNodes && hasChildren) { expandedTreeNodes.value.push(activeComponentId.value) } } @@ -330,25 +338,23 @@ function handleArrowDown() { return false } else { - const listIndex = treeNodeLinkedList.value.findIndex(arr => arr.includes(activeComponentId.value)) - const nextTree = treeNodeLinkedList.value[listIndex + 1] - if (nextTree) { - activeComponentId.value = nextTree[1] - } + activeComponentId.value = getNearestNextNode() } } function handleArrowUp() { - const isSubTreeRoot = treeNodeLinkedList.value.some(chain => chain[1] === activeComponentId.value) + const activeId = activeComponentId.value + const list = treeNodeLinkedList.value.find(item => item.includes(activeId)) + if (!list) + return - if (isSubTreeRoot) { - activeComponentId.value = getPrevExpandedNode() - } - else { - const currentIndex = flattenedTreeNodesIds.value.indexOf(activeComponentId.value) - if (currentIndex > 0) { - activeComponentId.value = flattenedTreeNodesIds.value[currentIndex - 1] - } + const activeItemListIndex = list.indexOf(activeId) + const activeItemParentIndex = activeItemListIndex > 0 ? activeItemListIndex - 1 : 0 + const parentId = list[activeItemParentIndex] + + const element = getNearestPreviousNode(parentId) + if (element) { + activeComponentId.value = element.id } } @@ -363,13 +369,51 @@ function handleEnter() { else expandedTreeNodes.value.splice(index, 1) } -function getPrevExpandedNode() { - const list = treeNodeLinkedList.value - const listIndex = list.findIndex((chain: string[]) => chain.includes(activeComponentId.value)) - if (listIndex > 0) { - return list[listIndex - 1].find(id => !expandedTreeNodes.value.includes(id)) || list[listIndex - 1][1] +function getNearestPreviousNode(parentId: string) { + const parentNode = flattenedTreeNodes.value.find(item => item.id === parentId) + if (!parentNode || !parentNode.children?.length) + return parentNode + + if (parentNode.children.length === 1) + return parentNode + + const indexInSiblings = parentNode?.children?.findIndex(item => item.id === activeComponentId.value) + + if (indexInSiblings <= 0) + return parentNode + + let prevSiblingNode = parentNode.children[indexInSiblings - 1] + + while (prevSiblingNode + && expandedTreeNodes.value.includes(prevSiblingNode.id) + && prevSiblingNode.children?.length) { + const lastChildNode = prevSiblingNode.children[prevSiblingNode.children.length - 1] + const next = getNearestPreviousNode(lastChildNode.id) + + if (!next || next.id === prevSiblingNode.id) + break + prevSiblingNode = next } - return list[listIndex][0] + + return prevSiblingNode || parentNode +} + +function getNearestNextNode() { + const linkedListTree = treeNodeLinkedList.value + const activeItemListIndex = [...linkedListTree] + .map((arr, index) => ({ arr, index })) + .reverse() + .find(({ arr }) => arr?.includes(activeComponentId.value)) + ?.index as number + + if (activeItemListIndex === -1) + return activeComponentId.value + const arr1 = linkedListTree[activeItemListIndex] + const arr2 = linkedListTree[activeItemListIndex + 1] + + const cloesestNodeIndex = arr2?.findIndex((val, index) => val !== arr1[index]) ?? -1 + + return cloesestNodeIndex !== -1 ? arr2[cloesestNodeIndex] : activeComponentId.value } function scrollToComponent() { From 717d5c5f4cda69305bdcfad7f152f000bf882893 Mon Sep 17 00:00:00 2001 From: mauryapari Date: Mon, 5 May 2025 19:41:40 +0530 Subject: [PATCH 3/5] Modified a condition to shorten it --- packages/applet/src/modules/components/index.vue | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/applet/src/modules/components/index.vue b/packages/applet/src/modules/components/index.vue index 60ee13b3..a7dd4721 100644 --- a/packages/applet/src/modules/components/index.vue +++ b/packages/applet/src/modules/components/index.vue @@ -400,14 +400,11 @@ function getNearestPreviousNode(parentId: string) { function getNearestNextNode() { const linkedListTree = treeNodeLinkedList.value - const activeItemListIndex = [...linkedListTree] - .map((arr, index) => ({ arr, index })) - .reverse() - .find(({ arr }) => arr?.includes(activeComponentId.value)) - ?.index as number + const activeItemListIndex = [...linkedListTree].findLastIndex(arr => arr?.includes(activeComponentId.value)) if (activeItemListIndex === -1) return activeComponentId.value + const arr1 = linkedListTree[activeItemListIndex] const arr2 = linkedListTree[activeItemListIndex + 1] From 53899871248748ea59088b7abdd1b5c695c57545 Mon Sep 17 00:00:00 2001 From: mauryapari Date: Sat, 16 Aug 2025 16:36:54 +0530 Subject: [PATCH 4/5] Changes as per review comment --- .../applet/src/modules/components/index.vue | 41 ++++--------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/packages/applet/src/modules/components/index.vue b/packages/applet/src/modules/components/index.vue index a7dd4721..6f99d285 100644 --- a/packages/applet/src/modules/components/index.vue +++ b/packages/applet/src/modules/components/index.vue @@ -8,10 +8,10 @@ import { import { parse } from '@vue/devtools-kit' import { isInChromePanel, isInSeparateWindow, sortByKey } from '@vue/devtools-shared' import { vTooltip, VueButton, VueDialog, VueInput } from '@vue/devtools-ui' -import { useElementSize, useEventListener, useToggle, watchDebounced } from '@vueuse/core' +import { onKeyStroke, useElementSize, useEventListener, useToggle, watchDebounced } from '@vueuse/core' import { flatten, groupBy } from 'lodash-es' import { Pane, Splitpanes } from 'splitpanes' -import { computed, nextTick, onUnmounted, ref, watch, watchEffect } from 'vue' +import { computed, onUnmounted, ref, watch, watchEffect } from 'vue' import SelectiveList from '~/components/basic/SelectiveList.vue' import RootStateViewer from '~/components/state/RootStateViewer.vue' import ComponentTree from '~/components/tree/TreeViewer.vue' @@ -279,38 +279,11 @@ useEventListener('keydown', (event) => { } }) -useEventListener('keydown', (event) => { - if (!activeComponentId.value) - return - - nextTick(() => { - switch (event.key) { - case 'ArrowRight':{ - handleArrowRight() - break - } - case 'ArrowLeft': { - handleArrowLeft() - break - } - case 'ArrowDown': { - handleArrowDown() - event.preventDefault() - return false - } - case 'ArrowUp': { - handleArrowUp() - event.preventDefault() - break - } - case ' ': - case 'Enter': { - handleEnter() - break - } - } - }) -}) +onKeyStroke('ArrowRight', handleArrowRight) +onKeyStroke('ArrowLeft', handleArrowLeft) +onKeyStroke('ArrowDown', handleArrowDown) +onKeyStroke('ArrowUp', handleArrowUp) +onKeyStroke([' ', 'Enter'], handleEnter) function handleArrowRight() { const isPresentInExpandedNodes = expandedTreeNodes.value.includes(activeComponentId.value) From 3504c8def264923f533cd2ecbc44c0b697da1698 Mon Sep 17 00:00:00 2001 From: mauryapari Date: Sat, 16 Aug 2025 18:03:27 +0530 Subject: [PATCH 5/5] Changes as per review comment --- .../applet/src/modules/components/index.vue | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/applet/src/modules/components/index.vue b/packages/applet/src/modules/components/index.vue index 6f99d285..a9ec2d88 100644 --- a/packages/applet/src/modules/components/index.vue +++ b/packages/applet/src/modules/components/index.vue @@ -279,28 +279,20 @@ useEventListener('keydown', (event) => { } }) -onKeyStroke('ArrowRight', handleArrowRight) -onKeyStroke('ArrowLeft', handleArrowLeft) -onKeyStroke('ArrowDown', handleArrowDown) -onKeyStroke('ArrowUp', handleArrowUp) -onKeyStroke([' ', 'Enter'], handleEnter) - -function handleArrowRight() { +onKeyStroke('ArrowRight', () => { const isPresentInExpandedNodes = expandedTreeNodes.value.includes(activeComponentId.value) const hasChildren = flattenedTreeNodes.value.find(item => item.id === activeComponentId.value)?.children?.length if (!isPresentInExpandedNodes && hasChildren) { expandedTreeNodes.value.push(activeComponentId.value) } -} - -function handleArrowLeft() { +}) +onKeyStroke('ArrowLeft', () => { if (expandedTreeNodes.value.includes(activeComponentId.value)) { expandedTreeNodes.value.splice(expandedTreeNodes.value.indexOf(activeComponentId.value), 1) } -} - -function handleArrowDown() { +}) +onKeyStroke('ArrowDown', () => { const activeComponentIdIndex = flattenedTreeNodesIds.value.indexOf(activeComponentId.value) const isActiveComponentExpanded = expandedTreeNodes.value.includes(activeComponentId.value) @@ -313,9 +305,9 @@ function handleArrowDown() { else { activeComponentId.value = getNearestNextNode() } -} +}) -function handleArrowUp() { +onKeyStroke('ArrowUp', () => { const activeId = activeComponentId.value const list = treeNodeLinkedList.value.find(item => item.includes(activeId)) if (!list) @@ -329,9 +321,9 @@ function handleArrowUp() { if (element) { activeComponentId.value = element.id } -} +}) -function handleEnter() { +onKeyStroke([' ', 'Enter'], () => { const node = flattenedTreeNodes.value.find(item => item.id === activeComponentId.value) if (!node?.children?.length) return @@ -340,7 +332,7 @@ function handleEnter() { if (index === -1) expandedTreeNodes.value.push(activeComponentId.value) else expandedTreeNodes.value.splice(index, 1) -} +}) function getNearestPreviousNode(parentId: string) { const parentNode = flattenedTreeNodes.value.find(item => item.id === parentId)