Skip to content

Commit 5ddb9df

Browse files
committed
feat(custom inspector): keybord navigation
1 parent 1428426 commit 5ddb9df

3 files changed

Lines changed: 108 additions & 8 deletions

File tree

packages/app-frontend/src/features/inspector/custom/CustomInspector.vue

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export default defineComponent({
1919
const {
2020
currentInspector: inspector,
2121
refreshInspector,
22-
refreshTree
22+
refreshTree,
23+
selectNode
2324
} = useCurrentInspector()
2425
2526
watch(() => inspector.value && inspector.value.treeFilter, () => {
@@ -37,10 +38,26 @@ export default defineComponent({
3738
const treeScroller = ref()
3839
provide('treeScroller', treeScroller)
3940
41+
// Keyboard
42+
43+
function selectNextChild (index) {
44+
if (index + 1 < inspector.value.rootNodes.length) {
45+
selectNode(inspector.value.rootNodes[index + 1])
46+
}
47+
}
48+
49+
function selectPreviousChild (index) {
50+
if (index - 1 >= 0) {
51+
selectNode(inspector.value.rootNodes[index - 1])
52+
}
53+
}
54+
4055
return {
4156
inspector,
4257
refreshInspector,
43-
treeScroller
58+
treeScroller,
59+
selectNextChild,
60+
selectPreviousChild
4461
}
4562
}
4663
})
@@ -65,9 +82,11 @@ export default defineComponent({
6582
class="flex-1 p-2 overflow-auto"
6683
>
6784
<CustomInspectorNode
68-
v-for="node of inspector.rootNodes"
85+
v-for="(node, index) of inspector.rootNodes"
6986
:key="node.id"
7087
:node="node"
88+
@select-next-sibling="selectNextChild(index)"
89+
@select-previous-sibling="selectPreviousChild(index)"
7190
/>
7291
</div>
7392
</div>

packages/app-frontend/src/features/inspector/custom/CustomInspectorNode.vue

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
<script lang="ts">
2+
import Vue from 'vue'
23
import { ref, computed, watch, defineComponent } from '@vue/composition-api'
34
import scrollIntoView from 'scroll-into-view-if-needed'
5+
import { onKeyDown } from '@front/util/keyboard'
46
import { useCurrentInspector } from './composable'
57
68
const DEFAULT_EXPAND_DEPTH = 2
@@ -20,13 +22,23 @@ export default defineComponent({
2022
}
2123
},
2224
23-
setup (props) {
25+
setup (props, { emit }) {
2426
const {
2527
currentInspector: inspector,
2628
selectNode
2729
} = useCurrentInspector()
2830
29-
const expanded = ref(props.depth < DEFAULT_EXPAND_DEPTH)
31+
const expanded = computed({
32+
get: () => !!inspector.value.expandedMap[props.node.id],
33+
set: value => {
34+
Vue.set(inspector.value.expandedMap, props.node.id, value)
35+
}
36+
})
37+
38+
// Init expanded
39+
if (props.node.expanded == null) {
40+
expanded.value = inspector.value.expandedMap[props.node.id] ?? props.depth < DEFAULT_EXPAND_DEPTH
41+
}
3042
3143
function toggle () {
3244
expanded.value = !expanded.value
@@ -67,12 +79,77 @@ export default defineComponent({
6779
watch(selected, () => autoScroll())
6880
watch(toggleEl, () => autoScroll())
6981
82+
// Keyboard
83+
84+
onKeyDown(event => {
85+
if (selected.value) {
86+
requestAnimationFrame(() => {
87+
switch (event.key) {
88+
case 'ArrowRight': {
89+
if (!expanded.value) {
90+
toggle()
91+
}
92+
break
93+
}
94+
case 'ArrowLeft': {
95+
if (expanded.value) {
96+
toggle()
97+
}
98+
break
99+
}
100+
case 'ArrowDown': {
101+
if (expanded.value && props.node.children.length) {
102+
// Select first child
103+
selectNode(props.node.children[0])
104+
} else {
105+
emit('select-next-sibling')
106+
}
107+
break
108+
}
109+
case 'ArrowUp': {
110+
emit('select-previous-sibling')
111+
}
112+
}
113+
})
114+
}
115+
})
116+
117+
function selectNextSibling (index) {
118+
if (index + 1 >= props.node.children.length) {
119+
emit('select-next-sibling')
120+
} else {
121+
selectNode(props.node.children[index + 1])
122+
}
123+
}
124+
125+
function selectPreviousSibling (index) {
126+
if (index === 0 || !props.node.children.length) {
127+
if (selected.value) {
128+
emit('select-previous-sibling')
129+
} else {
130+
select()
131+
}
132+
} else {
133+
let child = props.node.children[index - 1]
134+
while (child) {
135+
if (child.children.length && child.expanded) {
136+
child = child.children[child.children.length - 1]
137+
} else {
138+
selectNode(child)
139+
child = null
140+
}
141+
}
142+
}
143+
}
144+
70145
return {
71146
expanded,
72147
toggle,
73148
select,
74149
selected,
75-
toggleEl
150+
toggleEl,
151+
selectNextSibling,
152+
selectPreviousSibling
76153
}
77154
}
78155
})
@@ -130,10 +207,12 @@ export default defineComponent({
130207

131208
<div v-if="expanded && node.children">
132209
<CustomInspectorNode
133-
v-for="child in node.children"
210+
v-for="(child, index) in node.children"
134211
:key="child.id"
135212
:node="child"
136213
:depth="depth + 1"
214+
@select-next-sibling="selectNextSibling(index)"
215+
@select-previous-sibling="selectPreviousSibling(index)"
137216
/>
138217
</div>
139218
</div>

packages/app-frontend/src/features/inspector/custom/composable.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface Inspector extends InspectorFromBackend {
2222
selectedNode: any
2323
stateFilter: string
2424
state: any
25+
expandedMap: Record<string, boolean>
2526
}
2627

2728
const SELECTED_NODES_STORAGE = 'custom-inspector-selected-nodes'
@@ -35,7 +36,8 @@ function inspectorFactory (options: InspectorFromBackend): Inspector {
3536
selectedNodeId: selectedIdsStorage[options.id] || null,
3637
selectedNode: null,
3738
stateFilter: '',
38-
state: null
39+
state: null,
40+
expandedMap: {}
3941
}
4042
}
4143

0 commit comments

Comments
 (0)