Tree views
A tree in a container, fed by a provider. You supply nodes; the host owns rendering, selection, and expansion.
import { function defineMod<const TMod extends ModDefinition>(definition: TMod & ValidateModHooks<TMod>): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod, function treeView<TElement extends object, TContextValue extends string = string>(input: TreeViewInput<TElement, TContextValue>): TreeViewController<TElement, TContextValue>Declare a panel whose content is a tree the host renders. You supply a data provider, `getChildren` and `getTreeItem`, and the host draws the rows, handles selection, and places any inline actions. Reach for this instead of {@link view } whenever the content is a hierarchy, because you get the workbench's own tree behaviour rather than a webview you have to style yourself. This is the one authoring constructor that returns a live object rather than plain declaration data. Hold on to the controller it gives you and call `refresh()` after your data changes: with no arguments, listeners receive `null`, meaning reload everything; with elements, they receive exactly those elements.treeView } from '@modular/sdk'; interface Chapter { readonly Chapter.id: stringid: string; readonly Chapter.heading: stringheading: string; } export defaultdefineMod<{ readonly metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }; readonly views: readonly [TreeViewController<Chapter, string>]; }>(definition: { readonly metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }; readonly views: readonly [TreeViewController<Chapter, string>]; }): ModThe entry point of the SDK. A mod's module default-exports one `defineMod` call and nothing else. The returned value is data. It registers nothing, starts nothing, and touches no host state. The host folds it twice. At build time it runs the module in a throwaway sandbox and reads the value to derive the serializable manifest. At runtime it folds the same value again to wire your handlers into their lanes: ext-host for commands, tools, effects, and hooks; the sandboxed renderer for views and widgets. That double fold is why the module must be pure. It is re-executed to produce the manifest, so anything at the top level, a file read, a network call, a timer, a `console` line, runs during a build with no host attached. Put behavior inside handlers: `run`, `activate`, `handle`. The `ValidateModHooks` constraint makes the call fail to typecheck when a `hook.beforeAction` targets an action that is neither a known host action nor `command:` plus one of this mod's own command ids.defineMod({metadata: { readonly displayName: "Panels"; readonly description: "Contributed surfaces"; }metadata: { displayName: "Panels"displayName: 'Panels', description: "Contributed surfaces"description: 'Contributed surfaces' }, views: readonly [TreeViewController<Chapter, string>]views: [ treeView<Chapter, string>(input: TreeViewInput<Chapter, string>): TreeViewController<Chapter, string>Declare a panel whose content is a tree the host renders. You supply a data provider, `getChildren` and `getTreeItem`, and the host draws the rows, handles selection, and places any inline actions. Reach for this instead of {@link view } whenever the content is a hierarchy, because you get the workbench's own tree behaviour rather than a webview you have to style yourself. This is the one authoring constructor that returns a live object rather than plain declaration data. Hold on to the controller it gives you and call `refresh()` after your data changes: with no arguments, listeners receive `null`, meaning reload everything; with elements, they receive exactly those elements.treeView<Chapter>({ id: stringid: 'panels.chapters', title: stringtitle: 'Chapters', container?: "left" | "right" | "bottom" | "anywhere" | undefinedcontainer: 'left', provider: TreeDataProvider<Chapter, string>provider: { TreeDataProvider<Chapter, string>.getChildren(parent: Chapter | null, ctx: TreeContext<Chapter>): readonly Chapter[] | Promise<readonly Chapter[]>getChildren: parent: Chapter | nullparent => (parent: Chapter | nullparent === null ? function loadChapters(): readonly Chapter[]loadChapters() : []), TreeDataProvider<Chapter, string>.getTreeItem(element: Chapter, ctx: TreeContext<Chapter>): TreeItem<string> | Promise<TreeItem<string>>getTreeItem: chapter: Chapterchapter => ({ TreeItem<string>.id: stringid: chapter: Chapterchapter.Chapter.id: stringid, TreeItem<string>.label: stringlabel: chapter: Chapterchapter.Chapter.heading: stringheading }), }, }), ], }); declare function function loadChapters(): readonly Chapter[]loadChapters(): readonly Chapter[];
A tree view is generic over its element type, so getTreeItem receives the same
Chapter that getChildren returned rather than an opaque node.
Members
TreeView — id, title, container,
icon, provider, inlineActions, when, kind, plus onDidChangeTreeData
and onSelect.
| Member | What it decides |
|---|---|
provider | where the nodes come from |
onDidChangeTreeData | how you tell the host the nodes changed |
onSelect | what happens when a person picks one |
container | where the tree is mounted |
inlineActions | the buttons on a row |
when | whether the tree appears at all |
Push, do not poll
onDidChangeTreeData exists because the host does not watch your data. If you
mutate what provider returns without firing it, the tree keeps showing the old
nodes and nothing reports an error.