mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Add ModelView method SetItemLayout (#10306)
* Add ModelView method SetItemLayout * Remove extra line break
This commit is contained in:
@@ -51,7 +51,8 @@ export function createViewContext(): ViewTestContext {
|
|||||||
removeItem: () => true,
|
removeItem: () => true,
|
||||||
insertItem: () => { },
|
insertItem: () => { },
|
||||||
items: [],
|
items: [],
|
||||||
setLayout: () => { }
|
setLayout: () => { },
|
||||||
|
setItemLayout: () => { }
|
||||||
};
|
};
|
||||||
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ describe('Manage Package Dialog', () => {
|
|||||||
removeItem: () => true,
|
removeItem: () => true,
|
||||||
insertItem: () => { },
|
insertItem: () => { },
|
||||||
items: components,
|
items: components,
|
||||||
setLayout: () => { }
|
setLayout: () => { },
|
||||||
|
setItemLayout: () => { }
|
||||||
};
|
};
|
||||||
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
||||||
});
|
});
|
||||||
|
|||||||
4
src/sql/azdata.proposed.d.ts
vendored
4
src/sql/azdata.proposed.d.ts
vendored
@@ -385,6 +385,10 @@ declare module 'azdata' {
|
|||||||
alwaysShowTabs?: boolean;
|
alwaysShowTabs?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Container<TLayout, TItemLayout> extends Component {
|
||||||
|
setItemLayout(component: Component, layout: TItemLayout): void;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TaskInfo {
|
export interface TaskInfo {
|
||||||
targetLocation?: string;
|
targetLocation?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,6 +249,31 @@ export class PanelComponent extends Disposable implements IThemable {
|
|||||||
this.selectTab(nextTabIndex);
|
this.selectTab(nextTabIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the specified tab with new config values
|
||||||
|
* @param tabId The id of the tab to update
|
||||||
|
* @param config The values to update the tab with
|
||||||
|
*/
|
||||||
|
public updateTab(tabId: string, config: { title?: string, iconClass?: string }): void {
|
||||||
|
// First find the tab and update it with the new values. Then manually refresh the
|
||||||
|
// tab header since it won't detect changes made to the corresponding tab by itself.
|
||||||
|
let tabHeader: TabHeaderComponent;
|
||||||
|
const tabHeaders = this._tabHeaders.toArray();
|
||||||
|
const tab = this._tabs.find((item, i) => {
|
||||||
|
if (item.identifier === tabId) {
|
||||||
|
tabHeader = tabHeaders?.length > i ? tabHeaders[i] : undefined;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tab) {
|
||||||
|
tab.title = config.title;
|
||||||
|
tab.iconClass = config.iconClass;
|
||||||
|
tabHeader?.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private findAndRemoveTabFromMRU(tab: TabComponent): void {
|
private findAndRemoveTabFromMRU(tab: TabComponent): void {
|
||||||
let mruIndex = firstIndex(this._mru, i => i === tab);
|
let mruIndex = firstIndex(this._mru, i => i === tab);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import 'vs/css!./media/tabHeader';
|
import 'vs/css!./media/tabHeader';
|
||||||
|
|
||||||
import { Component, AfterContentInit, OnDestroy, Input, Output, ElementRef, ViewChild, EventEmitter } from '@angular/core';
|
import { Component, AfterContentInit, OnDestroy, Input, Output, ElementRef, ViewChild, EventEmitter, ChangeDetectorRef, forwardRef, Inject } from '@angular/core';
|
||||||
|
|
||||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
@@ -21,8 +21,8 @@ import { CloseTabAction } from 'sql/base/browser/ui/panel/tabActions';
|
|||||||
template: `
|
template: `
|
||||||
<div #actionHeader role="tab" [attr.aria-selected]="tab.active" [attr.aria-label]="tab.title" class="tab-header" style="flex: 0 0; flex-direction: row;" [class.active]="tab.active" tabindex="0" (click)="selectTab(tab)" (keyup)="onKey($event)">
|
<div #actionHeader role="tab" [attr.aria-selected]="tab.active" [attr.aria-label]="tab.title" class="tab-header" style="flex: 0 0; flex-direction: row;" [class.active]="tab.active" tabindex="0" (click)="selectTab(tab)" (keyup)="onKey($event)">
|
||||||
<div class="tab" role="presentation">
|
<div class="tab" role="presentation">
|
||||||
<a #tabIcon></a>
|
<a #tabIcon *ngIf="showIcon && tab.iconClass" class="tabIcon codicon icon {{tab.iconClass}}"></a>
|
||||||
<a class="tabLabel" [class.active]="tab.active" [title]="tab.title" #tabLabel></a>
|
<a class="tabLabel" [class.active]="tab.active" [title]="tab.title" #tabLabel>{{tab.title}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div #actionbar style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" ></div>
|
<div #actionbar style="flex: 0 0 auto; align-self: end; margin-top: auto; margin-bottom: auto;" ></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,10 +40,10 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
|
|
||||||
@ViewChild('actionHeader', { read: ElementRef }) private _actionHeaderRef!: ElementRef;
|
@ViewChild('actionHeader', { read: ElementRef }) private _actionHeaderRef!: ElementRef;
|
||||||
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef!: ElementRef;
|
@ViewChild('actionbar', { read: ElementRef }) private _actionbarRef!: ElementRef;
|
||||||
@ViewChild('tabLabel', { read: ElementRef }) private _tabLabelRef!: ElementRef;
|
|
||||||
@ViewChild('tabIcon', { read: ElementRef }) private _tabIconRef!: ElementRef;
|
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +51,10 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
return this._actionHeaderRef.nativeElement;
|
return this._actionHeaderRef.nativeElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public refresh(): void {
|
||||||
|
this._cd.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
ngAfterContentInit(): void {
|
ngAfterContentInit(): void {
|
||||||
if (this.tab.canClose || this.tab.actions) {
|
if (this.tab.canClose || this.tab.actions) {
|
||||||
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
|
this._actionbar = new ActionBar(this._actionbarRef.nativeElement);
|
||||||
@@ -62,15 +66,6 @@ export class TabHeaderComponent extends Disposable implements AfterContentInit,
|
|||||||
this._actionbar.push(closeAction, { icon: true, label: false });
|
this._actionbar.push(closeAction, { icon: true, label: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabLabelContainer = this._tabLabelRef.nativeElement as HTMLElement;
|
|
||||||
if (this.showIcon && this.tab.iconClass) {
|
|
||||||
const tabIconContainer = this._tabIconRef.nativeElement as HTMLElement;
|
|
||||||
tabIconContainer.className = 'tabIcon codicon icon';
|
|
||||||
tabIconContainer.classList.add(this.tab.iconClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
tabLabelContainer.textContent = this.tab.title;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export interface IComponent extends IDisposable {
|
|||||||
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any, index?: number) => void;
|
addToContainer?: (componentDescriptor: IComponentDescriptor, config: any, index?: number) => void;
|
||||||
removeFromContainer?: (componentDescriptor: IComponentDescriptor) => void;
|
removeFromContainer?: (componentDescriptor: IComponentDescriptor) => void;
|
||||||
setLayout?: (layout: any) => void;
|
setLayout?: (layout: any) => void;
|
||||||
|
setItemLayout?: (componentDescriptor: IComponentDescriptor, config: any) => void;
|
||||||
getHtml: () => any;
|
getHtml: () => any;
|
||||||
setProperties?: (properties: { [key: string]: any; }) => void;
|
setProperties?: (properties: { [key: string]: any; }) => void;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export interface IModelView extends IView {
|
|||||||
addToContainer(containerId: string, item: IItemConfig, index?: number): void;
|
addToContainer(containerId: string, item: IItemConfig, index?: number): void;
|
||||||
removeFromContainer(containerId: string, item: IItemConfig): void;
|
removeFromContainer(containerId: string, item: IItemConfig): void;
|
||||||
setLayout(componentId: string, layout: any): void;
|
setLayout(componentId: string, layout: any): void;
|
||||||
|
setItemLayout(componentId: string, item: IItemConfig): void;
|
||||||
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
||||||
setDataProvider(handle: number, componentId: string, context: any): void;
|
setDataProvider(handle: number, componentId: string, context: any): void;
|
||||||
refreshDataProvider(componentId: string, item: any): void;
|
refreshDataProvider(componentId: string, item: any): void;
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
|||||||
return this.execModelViewAction(handle, (modelView) => modelView.setLayout(componentId, layout));
|
return this.execModelViewAction(handle, (modelView) => modelView.setLayout(componentId, layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$setItemLayout(handle: number, containerId: string, item: IItemConfig): Thenable<void> {
|
||||||
|
return this.execModelViewAction(handle, (modelView) => modelView.setItemLayout(containerId, item));
|
||||||
|
}
|
||||||
|
|
||||||
private onEvent(handle: number, componentId: string, eventArgs: any) {
|
private onEvent(handle: number, componentId: string, eventArgs: any) {
|
||||||
this._proxy.$handleEvent(handle, componentId, eventArgs);
|
this._proxy.$handleEvent(handle, componentId, eventArgs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs,
|
|||||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||||
import { firstIndex } from 'vs/base/common/arrays';
|
import { firstIndex } from 'vs/base/common/arrays';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||||
|
|
||||||
class ModelBuilderImpl implements azdata.ModelBuilder {
|
class ModelBuilderImpl implements azdata.ModelBuilder {
|
||||||
private nextComponentId: number;
|
private nextComponentId: number;
|
||||||
@@ -730,6 +731,15 @@ class ComponentWrapper implements azdata.Component {
|
|||||||
return this._proxy.$setLayout(this._handle, this.id, layout);
|
return this._proxy.$setLayout(this._handle, this.id, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setItemLayout(item: azdata.Component, itemLayout: any): boolean {
|
||||||
|
const itemConfig = this.itemConfigs.find(c => c.component.id === item.id);
|
||||||
|
if (itemConfig) {
|
||||||
|
itemConfig.config = itemLayout;
|
||||||
|
this._proxy.$setItemLayout(this._handle, this.id, itemConfig.toIItemConfig()).then(undefined, onUnexpectedError);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public updateProperties(properties: { [key: string]: any }): Thenable<void> {
|
public updateProperties(properties: { [key: string]: any }): Thenable<void> {
|
||||||
this.properties = assign(this.properties, properties);
|
this.properties = assign(this.properties, properties);
|
||||||
return this.notifyPropertyChanged();
|
return this.notifyPropertyChanged();
|
||||||
@@ -1740,11 +1750,19 @@ 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 {
|
updateTabs(tabs: (azdata.Tab | azdata.TabGroup)[]): void {
|
||||||
this.clearItems();
|
|
||||||
const itemConfigs = createFromTabs(tabs);
|
const itemConfigs = createFromTabs(tabs);
|
||||||
itemConfigs.forEach(itemConfig => {
|
// Go through all of the tabs and either update their layout if they already exist
|
||||||
this.addItem(itemConfig.component, itemConfig.config);
|
// or add them if they don't.
|
||||||
|
// We do not currently support reordering or removing tabs.
|
||||||
|
itemConfigs.forEach(newItemConfig => {
|
||||||
|
const existingTab = this.itemConfigs.find(itemConfig => newItemConfig.config.id === itemConfig.config.id);
|
||||||
|
if (existingTab) {
|
||||||
|
this.setItemLayout(existingTab.component, newItemConfig.config);
|
||||||
|
} else {
|
||||||
|
this.addItem(newItemConfig.component, newItemConfig.config);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -732,6 +732,7 @@ export interface MainThreadModelViewShape extends IDisposable {
|
|||||||
$addToContainer(handle: number, containerId: string, item: IItemConfig, index?: number): Thenable<void>;
|
$addToContainer(handle: number, containerId: string, item: IItemConfig, index?: number): Thenable<void>;
|
||||||
$removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void>;
|
$removeFromContainer(handle: number, containerId: string, item: IItemConfig): Thenable<void>;
|
||||||
$setLayout(handle: number, componentId: string, layout: any): Thenable<void>;
|
$setLayout(handle: number, componentId: string, layout: any): Thenable<void>;
|
||||||
|
$setItemLayout(handle: number, componentId: string, item: IItemConfig): Thenable<void>;
|
||||||
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
|
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
|
||||||
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
||||||
$validate(handle: number, componentId: string): Thenable<boolean>;
|
$validate(handle: number, componentId: string): Thenable<boolean>;
|
||||||
|
|||||||
@@ -352,6 +352,24 @@ export abstract class ContainerBase<T> extends ComponentBase {
|
|||||||
|
|
||||||
abstract setLayout(layout: any): void;
|
abstract setLayout(layout: any): void;
|
||||||
|
|
||||||
|
public setItemLayout(componentDescriptor: IComponentDescriptor, config: any): void {
|
||||||
|
if (!componentDescriptor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const item = this.items.find(item => item.descriptor.id === componentDescriptor.id && item.descriptor.type === componentDescriptor.type);
|
||||||
|
if (item) {
|
||||||
|
item.config = config;
|
||||||
|
this.onItemLayoutUpdated(item);
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unable to set item layout - unknown item ${componentDescriptor.id}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
protected onItemsUpdated(): void {
|
protected onItemsUpdated(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected onItemLayoutUpdated(item: ItemDescriptor<T>): void {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Inject, Input, OnDestroy, ViewChild } from '@angular/core';
|
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Inject, Input, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { NavigationBarLayout, PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
import { NavigationBarLayout, PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||||
import { TabType } from 'sql/base/browser/ui/panel/tab.component';
|
import { TabType } from 'sql/base/browser/ui/panel/tab.component';
|
||||||
import { ContainerBase } from 'sql/workbench/browser/modelComponents/componentBase';
|
import { ContainerBase, ItemDescriptor } from 'sql/workbench/browser/modelComponents/componentBase';
|
||||||
import { ComponentEventType, IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
|
import { ComponentEventType, IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces';
|
||||||
import 'vs/css!./media/tabbedPanel';
|
import 'vs/css!./media/tabbedPanel';
|
||||||
import { IUserFriendlyIcon, createIconCssClass } from 'sql/workbench/browser/modelComponents/iconUtils';
|
import { IUserFriendlyIcon, createIconCssClass } from 'sql/workbench/browser/modelComponents/iconUtils';
|
||||||
@@ -121,4 +121,8 @@ export default class TabbedPanelComponent extends ContainerBase<TabConfig> imple
|
|||||||
this._panel.selectTab(firstTabIndex);
|
this._panel.selectTab(firstTabIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onItemLayoutUpdated(item: ItemDescriptor<TabConfig>): void {
|
||||||
|
this._panel.updateTab(item.config.id, { title: item.config.title, iconClass: item.config.icon ? createIconCssClass(item.config.icon) : undefined });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,13 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
this.queueAction(componentId, (component) => component.setLayout(layout));
|
this.queueAction(componentId, (component) => component.setLayout(layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setItemLayout(containerId: string, itemConfig: IItemConfig): void {
|
||||||
|
let childDescriptor = this.modelStore.getComponentDescriptor(itemConfig.componentShape.id);
|
||||||
|
this.queueAction(containerId, (component) => {
|
||||||
|
component.setItemLayout(childDescriptor, itemConfig.config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setProperties(componentId: string, properties: { [key: string]: any; }): void {
|
setProperties(componentId: string, properties: { [key: string]: any; }): void {
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user