Skip to content

Commit adc3d21

Browse files
committed
feat!: update tree view APIs
1 parent 1ea6bd4 commit adc3d21

File tree

4 files changed

+100
-62
lines changed

4 files changed

+100
-62
lines changed

demo/src/treeView.ts

+33-24
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
1-
import { createSingletonComposable, useTreeView } from 'reactive-vscode'
1+
import type { TreeViewNode } from 'reactive-vscode'
2+
import { computed, createSingletonComposable, useTreeView } from 'reactive-vscode'
23
import { TreeItemCollapsibleState } from 'vscode'
4+
import { calledTimes } from './states'
35

46
export const useDemoTreeView = createSingletonComposable(() => {
5-
return useTreeView(
6-
'reactive-tree-view',
7-
[
8-
{
9-
data: 1,
10-
children: [
11-
{ data: 2 },
12-
{ data: 3 },
13-
],
14-
},
15-
{
16-
data: 4,
7+
function getRootNode(index: number) {
8+
return {
9+
children: [
10+
getChildNode(index * 10 + 1),
11+
getChildNode(index * 10 + 2),
12+
],
13+
treeItem: {
14+
label: `Root ${index}`,
15+
collapsibleState: TreeItemCollapsibleState.Expanded,
1716
},
18-
{
19-
data: 5,
20-
},
21-
],
22-
{
23-
getTreeItem(node) {
24-
return {
25-
label: `Item ${node.data}`,
26-
collapsibleState: node.children?.length ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None,
27-
}
17+
}
18+
}
19+
20+
function getChildNode(index: number) {
21+
return {
22+
treeItem: {
23+
label: `Child ${index}`,
24+
collapsibleState: TreeItemCollapsibleState.None,
2825
},
29-
},
26+
}
27+
}
28+
29+
const treeData = computed(() => {
30+
const roots: TreeViewNode[] = []
31+
for (let i = 0; i < calledTimes.value; i++)
32+
roots.push(getRootNode(i))
33+
return roots
34+
})
35+
36+
return useTreeView(
37+
'reactive-tree-view',
38+
treeData,
3039
)
3140
})

docs/guide/view.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ As described in the [official documentation](https://code.visualstudio.com/api/r
4242

4343
Here is an example of a tree view:
4444

45-
<<< @/snippets/treeView.ts
45+
<<< @/snippets/treeView.ts {35-41}
4646

4747
Then you can call the `useDemoTreeView` function every where to register the tree view and get the returned value:
4848

@@ -56,7 +56,9 @@ export = defineExtension(() => {
5656
})
5757
```
5858

59-
The `children` property in nodes is used to define the children of the node. Other properties are user-defined and can be used in the `getTreeItem` function. The `getTreeItem` function returns a `vscode::TreeItem` object from a node.
59+
The `children` property in nodes is used to define the children of the node. The `treeItem` propert is required and is used to define the tree item of the node. It should be a `vscode::TreeItem` object, or a promise that resolves to a `vscode::TreeItem` object.
60+
61+
If you want to trigger an update based on some reactive values that aren't tracked in `treeData`, you can pass them to the `watchSource` option.
6062

6163
::: details About `reactive::createSingletonComposable`
6264
`reactive::createSingletonComposable` is a helper function to create a singleton composable. It will only create the composable once and return the same instance every time it is called.

docs/snippets/treeView.ts

+36-14
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,45 @@
1+
import type { TreeViewNode } from 'reactive-vscode'
12
import { computed, createSingletonComposable, useTreeView } from 'reactive-vscode'
23
import { TreeItemCollapsibleState } from 'vscode'
34

45
export const useDemoTreeView = createSingletonComposable(() => {
5-
const rootNodes = computed(() => [
6-
{ data: 1, children: [{ data: 2 }] },
7-
{ data: 3 },
8-
])
9-
// return anything you want to expose
10-
return useTreeView(
6+
function getRootNode(index: number) {
7+
return {
8+
children: [
9+
getChildNode(index * 10 + 1),
10+
getChildNode(index * 10 + 2),
11+
],
12+
treeItem: {
13+
label: `Root ${index}`,
14+
collapsibleState: TreeItemCollapsibleState.Expanded,
15+
},
16+
}
17+
}
18+
19+
function getChildNode(index: number) {
20+
return {
21+
treeItem: {
22+
label: `Child ${index}`,
23+
collapsibleState: TreeItemCollapsibleState.None,
24+
},
25+
}
26+
}
27+
28+
const treeData = computed(() => {
29+
const roots: TreeViewNode[] = []
30+
for (let i = 1; i < 5; i++)
31+
roots.push(getRootNode(i))
32+
return roots
33+
})
34+
35+
const view = useTreeView(
1136
'reactive-tree-view',
12-
rootNodes,
37+
treeData,
1338
{
14-
title: () => `Tree with ${rootNodes.value.length} roots`,
15-
getTreeItem(node) {
16-
return {
17-
label: `Item ${node.data}`,
18-
collapsibleState: node.children?.length ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None,
19-
}
20-
},
39+
title: () => `Tree with ${treeData.value.length} roots`,
2140
},
2241
)
42+
43+
// return anything you want to expose
44+
return view
2345
})

packages/core/src/composables/useTreeView.ts

+27-22
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
1-
import type { MaybeRefOrGetter } from '@reactive-vscode/reactivity'
1+
import type { MaybeRefOrGetter, WatchSource } from '@reactive-vscode/reactivity'
22
import { toValue, watch } from '@reactive-vscode/reactivity'
3-
import type { TreeDataProvider, TreeView, TreeViewOptions, ViewBadge } from 'vscode'
4-
import { EventEmitter, window } from 'vscode'
3+
import type { TreeDataProvider, TreeItem, TreeView, TreeViewOptions, ViewBadge } from 'vscode'
4+
import { window } from 'vscode'
55
import { createKeyedComposable } from '../utils'
66
import { useDisposable } from './useDisposable'
7+
import { useEventEmitter } from './useEventEmitter'
78
import { useViewBadge } from './useViewBadge'
89
import { useViewTitle } from './useViewTitle'
910

1011
export interface TreeViewNode {
1112
readonly children?: this[]
13+
readonly treeItem: TreeItem | Thenable<TreeItem>
1214
}
1315

1416
export type UseTreeViewOptions<T> =
15-
| (
16-
& Omit<TreeViewOptions<T>, 'treeDataProvider'>
17-
& Pick<TreeDataProvider<T>, 'getTreeItem' | 'resolveTreeItem'>
18-
& {
19-
title?: MaybeRefOrGetter<string | undefined>
20-
badge?: MaybeRefOrGetter<ViewBadge | undefined>
21-
}
22-
)
23-
| TreeDataProvider<T>['getTreeItem']
17+
& Omit<TreeViewOptions<T>, 'treeDataProvider'>
18+
& Pick<TreeDataProvider<T>, 'resolveTreeItem'>
19+
& {
20+
title?: MaybeRefOrGetter<string | undefined>
21+
badge?: MaybeRefOrGetter<ViewBadge | undefined>
22+
/**
23+
* Additional watch source to trigger a change event. Useful when `treeItem` is a promise.
24+
*/
25+
watchSource?: WatchSource<any>
26+
}
2427

2528
/**
2629
* Register a tree view. See `vscode::window.createTreeView`.
@@ -31,22 +34,24 @@ export const useTreeView = createKeyedComposable(
3134
<T extends TreeViewNode>(
3235
viewId: string,
3336
treeData: MaybeRefOrGetter<T[]>,
34-
options: UseTreeViewOptions<T>,
37+
options?: UseTreeViewOptions<T>,
3538
): TreeView<T> => {
36-
const normalizedOptions = typeof options === 'function' ? { getTreeItem: options } : options
37-
const changeEventEmitter = new EventEmitter<void>()
39+
const changeEventEmitter = useEventEmitter<void>()
3840

3941
watch(treeData, () => changeEventEmitter.fire())
4042

43+
if (options?.watchSource)
44+
watch(options.watchSource, () => changeEventEmitter.fire())
45+
4146
const childrenToParentMap = new WeakMap<T, T>()
4247

4348
const view = useDisposable(window.createTreeView(viewId, {
44-
...normalizedOptions,
49+
...options,
4550
treeDataProvider: {
46-
...normalizedOptions,
51+
...options,
4752
onDidChangeTreeData: changeEventEmitter.event,
4853
getTreeItem(node: T) {
49-
return normalizedOptions.getTreeItem(node)
54+
return node.treeItem
5055
},
5156
getChildren(node?: T) {
5257
if (node) {
@@ -61,11 +66,11 @@ export const useTreeView = createKeyedComposable(
6166
},
6267
}))
6368

64-
if (normalizedOptions?.title)
65-
useViewTitle(view, normalizedOptions.title)
69+
if (options?.title)
70+
useViewTitle(view, options.title)
6671

67-
if (normalizedOptions?.badge)
68-
useViewBadge(view, normalizedOptions.badge)
72+
if (options?.badge)
73+
useViewBadge(view, options.badge)
6974

7075
return view
7176
},

0 commit comments

Comments
 (0)