mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
add ability to dynamically update tabs (#9911)
* add dashboard and tabbedPanel samples * add updateTabs to tabbedPanel component * add updateTabs to tabbedPanel component
This commit is contained in:
@@ -11,37 +11,33 @@ export async function openModelViewDashboard(context: vscode.ExtensionContext):
|
|||||||
dashboard.registerTabs(async (view: azdata.ModelView) => {
|
dashboard.registerTabs(async (view: azdata.ModelView) => {
|
||||||
// Tab with toolbar
|
// Tab with toolbar
|
||||||
const button = view.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
const button = view.modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||||
label: 'Run',
|
label: 'Add databases tab',
|
||||||
iconPath: {
|
iconPath: {
|
||||||
light: context.asAbsolutePath('images/compare.svg'),
|
light: context.asAbsolutePath('images/compare.svg'),
|
||||||
dark: context.asAbsolutePath('images/compare-inverse.svg')
|
dark: context.asAbsolutePath('images/compare-inverse.svg')
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
button.onDidClick(() => {
|
|
||||||
vscode.window.showInformationMessage('Run button clicked');
|
|
||||||
});
|
|
||||||
|
|
||||||
const toolbar = view.modelBuilder.toolbarContainer().withItems([button]).withLayout({
|
const toolbar = view.modelBuilder.toolbarContainer().withItems([button]).withLayout({
|
||||||
orientation: azdata.Orientation.Horizontal
|
orientation: azdata.Orientation.Horizontal
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
const textComponent1 = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: 'text 1' }).component();
|
const input1 = view.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({ value: 'input 1' }).component();
|
||||||
const homeTab: azdata.DashboardTab = {
|
const homeTab: azdata.DashboardTab = {
|
||||||
id: 'home',
|
id: 'home',
|
||||||
toolbar: toolbar,
|
toolbar: toolbar,
|
||||||
content: textComponent1,
|
content: input1,
|
||||||
title: 'Home',
|
title: 'Home',
|
||||||
icon: context.asAbsolutePath('images/home.svg') // icon can be the path of a svg file
|
icon: context.asAbsolutePath('images/home.svg') // icon can be the path of a svg file
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tab with nested tabbed Panel
|
// Tab with nested tabbed Panel
|
||||||
const textComponent2 = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: 'text 2' }).component();
|
const addTabButton = view.modelBuilder.button().withProperties<azdata.ButtonProperties>({ label: 'Add a tab', width: '150px' }).component();
|
||||||
const textComponent3 = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: 'text 3' }).component();
|
const removeTabButton = view.modelBuilder.button().withProperties<azdata.ButtonProperties>({ label: 'Remove a tab', width: '150px' }).component();
|
||||||
|
const container = view.modelBuilder.flexContainer().withItems([addTabButton, removeTabButton]).withLayout({ flexFlow: 'column' }).component();
|
||||||
const nestedTab1 = {
|
const nestedTab1 = {
|
||||||
title: 'Tab1',
|
title: 'Tab1',
|
||||||
content: textComponent2,
|
content: container,
|
||||||
id: 'tab1',
|
id: 'tab1',
|
||||||
icon: {
|
icon: {
|
||||||
light: context.asAbsolutePath('images/user.svg'),
|
light: context.asAbsolutePath('images/user.svg'),
|
||||||
@@ -49,9 +45,10 @@ export async function openModelViewDashboard(context: vscode.ExtensionContext):
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const input2 = view.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({ value: 'input 2' }).component();
|
||||||
const nestedTab2 = {
|
const nestedTab2 = {
|
||||||
title: 'Tab2',
|
title: 'Tab2',
|
||||||
content: textComponent3,
|
content: input2,
|
||||||
icon: {
|
icon: {
|
||||||
light: context.asAbsolutePath('images/group.svg'),
|
light: context.asAbsolutePath('images/group.svg'),
|
||||||
dark: context.asAbsolutePath('images/group_inverse.svg')
|
dark: context.asAbsolutePath('images/group_inverse.svg')
|
||||||
@@ -59,6 +56,13 @@ export async function openModelViewDashboard(context: vscode.ExtensionContext):
|
|||||||
id: 'tab2'
|
id: 'tab2'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const input3 = view.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({ value: 'input 4' }).component();
|
||||||
|
const nestedTab3 = {
|
||||||
|
title: 'Tab3',
|
||||||
|
content: input3,
|
||||||
|
id: 'tab3'
|
||||||
|
};
|
||||||
|
|
||||||
const tabbedPanel = view.modelBuilder.tabbedPanel().withTabs([
|
const tabbedPanel = view.modelBuilder.tabbedPanel().withTabs([
|
||||||
nestedTab1, nestedTab2
|
nestedTab1, nestedTab2
|
||||||
]).withLayout({
|
]).withLayout({
|
||||||
@@ -66,6 +70,15 @@ export async function openModelViewDashboard(context: vscode.ExtensionContext):
|
|||||||
showIcon: true
|
showIcon: true
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
|
|
||||||
|
addTabButton.onDidClick(() => {
|
||||||
|
tabbedPanel.updateTabs([nestedTab1, nestedTab2, nestedTab3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
removeTabButton.onDidClick(() => {
|
||||||
|
tabbedPanel.updateTabs([nestedTab1, nestedTab3]);
|
||||||
|
});
|
||||||
|
|
||||||
const settingsTab: azdata.DashboardTab = {
|
const settingsTab: azdata.DashboardTab = {
|
||||||
id: 'settings',
|
id: 'settings',
|
||||||
content: tabbedPanel,
|
content: tabbedPanel,
|
||||||
@@ -81,10 +94,23 @@ export async function openModelViewDashboard(context: vscode.ExtensionContext):
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Databases tab
|
||||||
|
const databasesText = view.modelBuilder.inputBox().withProperties<azdata.InputBoxProperties>({ value: 'This is databases tab', width: '200px' }).component();
|
||||||
|
const databasesTab: azdata.DashboardTab = {
|
||||||
|
id: 'databases',
|
||||||
|
content: databasesText,
|
||||||
|
title: 'Databases',
|
||||||
|
icon: context.asAbsolutePath('images/default.svg')
|
||||||
|
};
|
||||||
|
button.onDidClick(() => {
|
||||||
|
dashboard.updateTabs([homeTab, databasesTab, securityTabGroup]);
|
||||||
|
});
|
||||||
|
|
||||||
return [
|
return [
|
||||||
homeTab,
|
homeTab,
|
||||||
securityTabGroup
|
securityTabGroup
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
dashboard.open();
|
await dashboard.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
src/sql/azdata.proposed.d.ts
vendored
7
src/sql/azdata.proposed.d.ts
vendored
@@ -210,6 +210,12 @@ declare module 'azdata' {
|
|||||||
* The event argument is the id of the selected tab.
|
* The event argument is the id of the selected tab.
|
||||||
*/
|
*/
|
||||||
onTabChanged: vscode.Event<string>;
|
onTabChanged: vscode.Event<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update the tabs.
|
||||||
|
* @param tabs new tabs
|
||||||
|
*/
|
||||||
|
updateTabs(tabs: (Tab | TabGroup)[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -310,6 +316,7 @@ declare module 'azdata' {
|
|||||||
export interface ModelViewDashboard {
|
export interface ModelViewDashboard {
|
||||||
registerTabs(handler: (view: ModelView) => Thenable<(DashboardTab | DashboardTabGroup)[]>): void;
|
registerTabs(handler: (view: ModelView) => Thenable<(DashboardTab | DashboardTabGroup)[]>): void;
|
||||||
open(): Thenable<void>;
|
open(): Thenable<void>;
|
||||||
|
updateTabs(tabs: (DashboardTab | DashboardTabGroup)[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createModelViewDashboard(title: string, options?: ModelViewDashboardOptions): ModelViewDashboard;
|
export function createModelViewDashboard(title: string, options?: ModelViewDashboardOptions): ModelViewDashboard;
|
||||||
|
|||||||
@@ -495,29 +495,33 @@ class ToolbarContainerBuilder extends GenericContainerBuilder<azdata.ToolbarCont
|
|||||||
|
|
||||||
class TabbedPanelComponentBuilder extends ContainerBuilderImpl<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> implements azdata.TabbedPanelComponentBuilder {
|
class TabbedPanelComponentBuilder extends ContainerBuilderImpl<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> implements azdata.TabbedPanelComponentBuilder {
|
||||||
withTabs(items: (azdata.Tab | azdata.TabGroup)[]): azdata.ContainerBuilder<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> {
|
withTabs(items: (azdata.Tab | azdata.TabGroup)[]): azdata.ContainerBuilder<azdata.TabbedPanelComponent, azdata.TabbedPanelLayout, any> {
|
||||||
|
this._component.itemConfigs = createFromTabs(items);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFromTabs(items: (azdata.Tab | azdata.TabGroup)[]): InternalItemConfig[] {
|
||||||
const itemConfigs = [];
|
const itemConfigs = [];
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (item && 'tabs' in item) {
|
if (item && 'tabs' in item) {
|
||||||
item.tabs.forEach(tab => {
|
item.tabs.forEach(tab => {
|
||||||
itemConfigs.push(this.toItemConfig(tab.content, tab.title, tab.id, item.title, tab.icon));
|
itemConfigs.push(toTabItemConfig(tab.content, tab.title, tab.id, item.title, tab.icon));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const tab = <azdata.Tab>item;
|
const tab = <azdata.Tab>item;
|
||||||
itemConfigs.push(this.toItemConfig(tab.content, tab.title, tab.id, undefined, tab.icon));
|
itemConfigs.push(toTabItemConfig(tab.content, tab.title, tab.id, undefined, tab.icon));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._component.itemConfigs = itemConfigs;
|
return itemConfigs;
|
||||||
return this;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
toItemConfig(content: azdata.Component, title: string, id?: string, group?: string, icon?: string | URI | { light: string | URI; dark: string | URI }): InternalItemConfig {
|
function toTabItemConfig(content: azdata.Component, title: string, id?: string, group?: string, icon?: string | URI | { light: string | URI; dark: string | URI }): InternalItemConfig {
|
||||||
return new InternalItemConfig(content as ComponentWrapper, {
|
return new InternalItemConfig(content as ComponentWrapper, {
|
||||||
title: title,
|
title: title,
|
||||||
group: group,
|
group: group,
|
||||||
id: id,
|
id: id,
|
||||||
icon: icon
|
icon: icon
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadingComponentBuilder extends ComponentBuilderImpl<azdata.LoadingComponent> implements azdata.LoadingComponentBuilder {
|
class LoadingComponentBuilder extends ComponentBuilderImpl<azdata.LoadingComponent> implements azdata.LoadingComponentBuilder {
|
||||||
@@ -1728,6 +1732,13 @@ class TabbedPanelComponentWrapper extends ComponentWrapper implements azdata.Tab
|
|||||||
this.properties = {};
|
this.properties = {};
|
||||||
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<string>());
|
this._emitterMap.set(ComponentEventType.onDidChange, new Emitter<string>());
|
||||||
}
|
}
|
||||||
|
updateTabs(tabs: (azdata.Tab | azdata.TabGroup)[]): void {
|
||||||
|
this.clearItems();
|
||||||
|
const itemConfigs = createFromTabs(tabs);
|
||||||
|
itemConfigs.forEach(itemConfig => {
|
||||||
|
this.addItem(itemConfig.component, itemConfig.config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public get onTabChanged(): vscode.Event<string> {
|
public get onTabChanged(): vscode.Event<string> {
|
||||||
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
let emitter = this._emitterMap.get(ComponentEventType.onDidChange);
|
||||||
|
|||||||
@@ -458,34 +458,34 @@ class WizardImpl implements azdata.window.Wizard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ModelViewDashboardImpl implements azdata.window.ModelViewDashboard {
|
class ModelViewDashboardImpl implements azdata.window.ModelViewDashboard {
|
||||||
|
private _tabbedPanel: azdata.TabbedPanelComponent;
|
||||||
|
private _view: azdata.ModelView;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _editor: ModelViewEditorImpl,
|
private _editor: ModelViewEditorImpl,
|
||||||
private _options?: azdata.ModelViewDashboardOptions
|
private _options?: azdata.ModelViewDashboardOptions
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTabs(tabs: (azdata.DashboardTab | azdata.DashboardTabGroup)[]): void {
|
||||||
|
if (this._tabbedPanel === undefined || this._view === undefined) {
|
||||||
|
throw new Error(nls.localize('dashboardNotInitialized', "Tabs are not initialized"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tabbedPanel.updateTabs(this.createTabs(tabs, this._view));
|
||||||
|
}
|
||||||
|
|
||||||
registerTabs(handler: (view: azdata.ModelView) => Thenable<(azdata.DashboardTab | azdata.DashboardTabGroup)[]>): void {
|
registerTabs(handler: (view: azdata.ModelView) => Thenable<(azdata.DashboardTab | azdata.DashboardTabGroup)[]>): void {
|
||||||
this._editor.registerContent(async (view) => {
|
this._editor.registerContent(async (view) => {
|
||||||
|
this._view = view;
|
||||||
const dashboardTabs = await handler(view);
|
const dashboardTabs = await handler(view);
|
||||||
const tabs: (azdata.TabGroup | azdata.Tab)[] = [];
|
const tabs = this.createTabs(dashboardTabs, view);
|
||||||
dashboardTabs.forEach((item: azdata.DashboardTab | azdata.DashboardTabGroup) => {
|
this._tabbedPanel = view.modelBuilder.tabbedPanel().withTabs(tabs).withLayout({
|
||||||
if ('tabs' in item) {
|
|
||||||
tabs.push(<azdata.TabGroup>{
|
|
||||||
title: item.title,
|
|
||||||
tabs: item.tabs.map(tab => {
|
|
||||||
return this.createTab(tab, view);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
tabs.push(this.createTab(item, view));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const tabbedPanel = view.modelBuilder.tabbedPanel().withTabs(tabs).withLayout({
|
|
||||||
orientation: 'vertical',
|
orientation: 'vertical',
|
||||||
showIcon: this._options?.showIcon ?? true,
|
showIcon: this._options?.showIcon ?? true,
|
||||||
alwaysShowTabs: this._options?.alwaysShowTabs ?? false
|
alwaysShowTabs: this._options?.alwaysShowTabs ?? false
|
||||||
}).component();
|
}).component();
|
||||||
return view.initializeModel(tabbedPanel);
|
return view.initializeModel(this._tabbedPanel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,6 +508,23 @@ class ModelViewDashboardImpl implements azdata.window.ModelViewDashboard {
|
|||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createTabs(dashboardTabs: (azdata.DashboardTab | azdata.DashboardTabGroup)[], view: azdata.ModelView): (azdata.TabGroup | azdata.Tab)[] {
|
||||||
|
const tabs: (azdata.TabGroup | azdata.Tab)[] = [];
|
||||||
|
dashboardTabs.forEach((item: azdata.DashboardTab | azdata.DashboardTabGroup) => {
|
||||||
|
if ('tabs' in item) {
|
||||||
|
tabs.push(<azdata.TabGroup>{
|
||||||
|
title: item.title,
|
||||||
|
tabs: item.tabs.map(tab => {
|
||||||
|
return this.createTab(tab, view);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tabs.push(this.createTab(item, view));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||||
|
|||||||
@@ -352,6 +352,7 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
|||||||
|
|
||||||
public clearContainer(): void {
|
public clearContainer(): void {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
|
this.onItemsUpdated();
|
||||||
this._changeRef.detectChanges();
|
this._changeRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,12 @@ export default class TabbedPanelComponent extends ContainerBase<TabConfig> imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
onItemsUpdated(): void {
|
onItemsUpdated(): void {
|
||||||
const firstTabIndex = this.tabs.findIndex(tab => tab.type === 'tab');
|
if (this.items.length === 0) {
|
||||||
|
this._itemIndexToProcess = 0;
|
||||||
|
this._tabs = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstTabIndex = this._tabs.findIndex(tab => tab.type === 'tab');
|
||||||
if (firstTabIndex >= 0) {
|
if (firstTabIndex >= 0) {
|
||||||
this._panel.selectTab(firstTabIndex);
|
this._panel.selectTab(firstTabIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user