table designer improvements (#18206)

* new table experience

* new table info for existing table

* allow delete confirmation

* fix editor lock isue

* vbump sts

* PR feedback
This commit is contained in:
Alan Ren
2022-02-02 18:46:09 -08:00
committed by GitHub
parent c35eae5969
commit c535c2dbde
14 changed files with 164 additions and 111 deletions

View File

@@ -1,6 +1,6 @@
{ {
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "3.0.0-release.192", "version": "3.0.0-release.193",
"downloadFileNames": { "downloadFileNames": {
"Windows_86": "win-x86-net6.0.zip", "Windows_86": "win-x86-net6.0.zip",
"Windows_64": "win-x64-net6.0.zip", "Windows_64": "win-x64-net6.0.zip",

View File

@@ -1056,7 +1056,7 @@ export namespace ProcessTableDesignerEditRequest {
} }
export namespace PublishTableDesignerChangesRequest { export namespace PublishTableDesignerChangesRequest {
export const type = new RequestType<azdata.designers.TableInfo, void, void, void>('tabledesigner/publish'); export const type = new RequestType<azdata.designers.TableInfo, azdata.designers.PublishChangesResult, void, void>('tabledesigner/publish');
} }
export namespace TableDesignerGenerateScriptRequest { export namespace TableDesignerGenerateScriptRequest {

View File

@@ -1131,7 +1131,7 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
} }
}; };
const publishChanges = (tableInfo: azdata.designers.TableInfo): Thenable<void> => { const publishChanges = (tableInfo: azdata.designers.TableInfo): Thenable<azdata.designers.PublishChangesResult> => {
try { try {
return client.sendRequest(contracts.PublishTableDesignerChangesRequest.type, tableInfo); return client.sendRequest(contracts.PublishTableDesignerChangesRequest.type, tableInfo);
} }

View File

@@ -31,7 +31,6 @@ export function registerTableDesignerCommands(appContext: AppContext) {
const schema = context.nodeInfo.metadata.schema; const schema = context.nodeInfo.metadata.schema;
const name = context.nodeInfo.metadata.name; const name = context.nodeInfo.metadata.name;
const connectionString = await azdata.connection.getConnectionString(context.connectionProfile.id, true); const connectionString = await azdata.connection.getConnectionString(context.connectionProfile.id, true);
const connectionUri = await azdata.connection.getUriForConnection(context.connectionProfile.id);
const serverInfo = await azdata.connection.getServerInfo(context.connectionProfile.id); const serverInfo = await azdata.connection.getServerInfo(context.connectionProfile.id);
let telemetryInfo: ITelemetryEventProperties = {}; let telemetryInfo: ITelemetryEventProperties = {};
telemetryInfo = Telemetry.fillServerInfo(telemetryInfo, serverInfo); telemetryInfo = Telemetry.fillServerInfo(telemetryInfo, serverInfo);
@@ -41,7 +40,7 @@ export function registerTableDesignerCommands(appContext: AppContext) {
isNewTable: false, isNewTable: false,
name: name, name: name,
schema: schema, schema: schema,
id: `${connectionUri}|${database}|${schema}|${name}`, id: `${connectionString}|${database}|${schema}|${name}`,
connectionString: connectionString connectionString: connectionString
}, telemetryInfo); }, telemetryInfo);
})); }));

View File

@@ -1042,7 +1042,7 @@ declare module 'azdata' {
* Publish the changes. * Publish the changes.
* @param table the table information * @param table the table information
*/ */
publishChanges(table: TableInfo): Thenable<void>; publishChanges(table: TableInfo): Thenable<PublishChangesResult>;
/** /**
* Generate script for the changes. * Generate script for the changes.
@@ -1154,7 +1154,7 @@ declare module 'azdata' {
*/ */
export enum TableForeignKeyProperty { export enum TableForeignKeyProperty {
Name = 'name', Name = 'name',
PrimaryKeyTable = 'primaryKeyTable', ForeignTable = 'foreignTable',
OnDeleteAction = 'onDeleteAction', OnDeleteAction = 'onDeleteAction',
OnUpdateAction = 'onUpdateAction', OnUpdateAction = 'onUpdateAction',
Columns = 'columns' Columns = 'columns'
@@ -1164,8 +1164,8 @@ declare module 'azdata' {
* Name of the columns mapping properties for foreign key. * Name of the columns mapping properties for foreign key.
*/ */
export enum ForeignKeyColumnMappingProperty { export enum ForeignKeyColumnMappingProperty {
PrimaryKeyColumn = 'primaryKeyColumn', Column = 'column',
ForeignKeyColumn = 'foreignKeyColumn' ForeignColumn = 'foreignColumn'
} }
/** /**
@@ -1238,7 +1238,7 @@ declare module 'azdata' {
indexColumnSpecificationTableOptions?: TableDesignerBuiltInTableViewOptions; indexColumnSpecificationTableOptions?: TableDesignerBuiltInTableViewOptions;
} }
export interface TableDesignerBuiltInTableViewOptions { export interface TableDesignerBuiltInTableViewOptions extends DesignerTablePropertiesBase {
/** /**
* Whether to show the table. Default value is false. * Whether to show the table. Default value is false.
*/ */
@@ -1247,14 +1247,6 @@ declare module 'azdata' {
* Properties to be displayed in the table, other properties can be accessed in the properties view. * Properties to be displayed in the table, other properties can be accessed in the properties view.
*/ */
propertiesToDisplay?: string[]; propertiesToDisplay?: string[];
/**
* Whether adding new rows is supported.
*/
canAddRows?: boolean;
/**
* Whether removing rows is supported.
*/
canRemoveRows?: boolean;
/** /**
* Additional properties for the entity. * Additional properties for the entity.
*/ */
@@ -1317,39 +1309,45 @@ declare module 'azdata' {
*/ */
export type DesignerComponentTypeName = 'input' | 'checkbox' | 'dropdown' | 'table'; export type DesignerComponentTypeName = 'input' | 'checkbox' | 'dropdown' | 'table';
/** export interface DesignerTablePropertiesBase {
* The properties for the table component in the designer.
*/
export interface DesignerTableProperties extends ComponentProperties {
/**
* the name of the properties to be displayed, properties not in this list will be accessible in properties pane.
*/
columns?: string[];
/**
* The display name of the object type.
*/
objectTypeDisplayName: string;
/**
* the properties of the table data item.
*/
itemProperties?: DesignerDataPropertyInfo[];
/**
* The data to be displayed.
*/
data?: DesignerTableComponentDataItem[];
/** /**
* Whether user can add new rows to the table. The default value is true. * Whether user can add new rows to the table. The default value is true.
*/ */
canAddRows?: boolean; canAddRows?: boolean;
/** /**
* Whether user can remove rows from the table. The default value is true. * Whether user can remove rows from the table. The default value is true.
*/ */
canRemoveRows?: boolean; canRemoveRows?: boolean;
/**
* Whether to show confirmation when user removes a row. The default value is false.
*/
showRemoveRowConfirmation?: boolean;
/**
* The confirmation message to be displayed when user removes a row.
*/
removeRowConfirmationMessage?: string;
}
/**
* The properties for the table component in the designer.
*/
export interface DesignerTableProperties extends ComponentProperties, DesignerTablePropertiesBase {
/**
* the name of the properties to be displayed, properties not in this list will be accessible in properties pane.
*/
columns?: string[];
/**
* The display name of the object type.
*/
objectTypeDisplayName: string;
/**
* the properties of the table data item.
*/
itemProperties?: DesignerDataPropertyInfo[];
/**
* The data to be displayed.
*/
data?: DesignerTableComponentDataItem[];
} }
/** /**
@@ -1429,6 +1427,16 @@ declare module 'azdata' {
*/ */
errors?: { message: string, property?: DesignerEditPath }[]; errors?: { message: string, property?: DesignerEditPath }[];
} }
/**
* The result returned by the table designer provider after handling the publish changes request.
*/
export interface PublishChangesResult {
/**
* The new table information after the changes are published.
*/
newTableInfo: TableInfo;
}
} }
export interface ExecutionPlanGraph { export interface ExecutionPlanGraph {

View File

@@ -519,7 +519,7 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
processTableEdit(table, edit): Thenable<azdata.designers.DesignerEditResult> { processTableEdit(table, edit): Thenable<azdata.designers.DesignerEditResult> {
return self._proxy.$processTableDesignerEdit(handle, table, edit); return self._proxy.$processTableDesignerEdit(handle, table, edit);
}, },
publishChanges(tableInfo: azdata.designers.TableInfo): Thenable<void> { publishChanges(tableInfo: azdata.designers.TableInfo): Thenable<azdata.designers.PublishChangesResult> {
return self._proxy.$publishTableDesignerChanges(handle, tableInfo); return self._proxy.$publishTableDesignerChanges(handle, tableInfo);
}, },
generateScript(tableInfo: azdata.designers.TableInfo): Thenable<string> { generateScript(tableInfo: azdata.designers.TableInfo): Thenable<string> {

View File

@@ -901,7 +901,7 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).processTableEdit(table, edit); return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).processTableEdit(table, edit);
} }
public override $publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable<void> { public override $publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable<azdata.designers.PublishChangesResult> {
return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).publishChanges(table); return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).publishChanges(table);
} }

View File

@@ -541,7 +541,7 @@ export abstract class ExtHostDataProtocolShape {
/** /**
* Publish the table designer changes. * Publish the table designer changes.
*/ */
$publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable<void> { throw ni(); } $publishTableDesignerChanges(handle: number, table: azdata.designers.TableInfo): Thenable<azdata.designers.PublishChangesResult> { throw ni(); }
/** /**
* Generate scripts. * Generate scripts.

View File

@@ -969,15 +969,15 @@ export namespace designers {
export enum TableForeignKeyProperty { export enum TableForeignKeyProperty {
Name = 'name', Name = 'name',
PrimaryKeyTable = 'primaryKeyTable', ForeignTable = 'foreignTable',
OnDeleteAction = 'onDeleteAction', OnDeleteAction = 'onDeleteAction',
OnUpdateAction = 'onUpdateAction', OnUpdateAction = 'onUpdateAction',
Columns = 'columns' Columns = 'columns'
} }
export enum ForeignKeyColumnMappingProperty { export enum ForeignKeyColumnMappingProperty {
PrimaryKeyColumn = 'primaryKeyColumn', Column = 'column',
ForeignKeyColumn = 'foreignKeyColumn' ForeignColumn = 'foreignColumn'
} }
export enum TableCheckConstraintProperty { export enum TableCheckConstraintProperty {

View File

@@ -36,6 +36,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { DesignerScriptEditor } from 'sql/workbench/browser/designer/designerScriptEditor'; import { DesignerScriptEditor } from 'sql/workbench/browser/designer/designerScriptEditor';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
export interface IDesignerStyle { export interface IDesignerStyle {
tabbedPanelStyles?: ITabbedPanelStyles; tabbedPanelStyles?: ITabbedPanelStyles;
@@ -53,7 +54,7 @@ export type DesignerUIComponent = InputBox | Checkbox | Table<Slick.SlickData> |
export type CreateComponentsFunc = (container: HTMLElement, components: DesignerDataPropertyInfo[], parentPath: DesignerEditPath) => DesignerUIComponent[]; export type CreateComponentsFunc = (container: HTMLElement, components: DesignerDataPropertyInfo[], parentPath: DesignerEditPath) => DesignerUIComponent[];
export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerViewModel) => void; export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerViewModel) => void;
const TableRowHeight = 23; const TableRowHeight = 25;
const TableHeaderRowHeight = 28; const TableHeaderRowHeight = 28;
type DesignerUIArea = 'PropertiesView' | 'ScriptView' | 'TopContentView' | 'TabsView'; type DesignerUIArea = 'PropertiesView' | 'ScriptView' | 'TopContentView' | 'TabsView';
@@ -85,7 +86,8 @@ export class Designer extends Disposable implements IThemable {
constructor(private readonly _container: HTMLElement, constructor(private readonly _container: HTMLElement,
@IInstantiationService private readonly _instantiationService: IInstantiationService, @IInstantiationService private readonly _instantiationService: IInstantiationService,
@IContextViewService private readonly _contextViewProvider: IContextViewService, @IContextViewService private readonly _contextViewProvider: IContextViewService,
@INotificationService private readonly _notificationService: INotificationService) { @INotificationService private readonly _notificationService: INotificationService,
@IDialogService private readonly _dialogService: IDialogService) {
super(); super();
this._tableCellEditorFactory = new TableCellEditorFactory( this._tableCellEditorFactory = new TableCellEditorFactory(
{ {
@@ -303,11 +305,23 @@ export class Designer extends Disposable implements IThemable {
const propertyName = edit.path[0] as string; const propertyName = edit.path[0] as string;
const tableData = this._input.viewModel[propertyName] as DesignerTableProperties; const tableData = this._input.viewModel[propertyName] as DesignerTableProperties;
const table = this._componentMap.get(propertyName).component as Table<Slick.SlickData>; const table = this._componentMap.get(propertyName).component as Table<Slick.SlickData>;
try {
table.setActiveCell(tableData.data.length - 1, 0); table.setActiveCell(tableData.data.length - 1, 0);
} }
catch {
// Ignore the slick grid error when setting active cell.
}
} else {
this.updatePropertiesPane(this._propertiesPane.objectPath);
}
} else if (edit.type === DesignerEditType.Update) { } else if (edit.type === DesignerEditType.Update) {
// for edit, update the properties pane with new values of current object. // for edit, update the properties pane with new values of current object.
this.updatePropertiesPane(this._propertiesPane.objectPath); this.updatePropertiesPane(this._propertiesPane.objectPath);
} else if (edit.type === DesignerEditType.Remove) {
// removing the secondary level entities, the properties pane needs to be updated to reflect the changes.
if (edit.path.length === 4) {
this.updatePropertiesPane(this._propertiesPane.objectPath);
}
} }
} catch (err) { } catch (err) {
this._notificationService.error(err); this._notificationService.error(err);
@@ -330,7 +344,7 @@ export class Designer extends Disposable implements IThemable {
let message; let message;
let timeout; let timeout;
switch (action) { switch (action) {
case 'save': case 'publish':
message = showLoading ? localize('designer.publishingChanges', "Publishing changes...") : localize('designer.publishChangesCompleted', "Changes have been published"); message = showLoading ? localize('designer.publishingChanges', "Publishing changes...") : localize('designer.publishChangesCompleted', "Changes have been published");
timeout = 0; timeout = 0;
break; break;
@@ -693,7 +707,17 @@ export class Designer extends Disposable implements IThemable {
resizable: false, resizable: false,
isFontIcon: true isFontIcon: true
}); });
deleteRowColumn.onClick((e) => { deleteRowColumn.onClick(async (e) => {
if (tableProperties.showRemoveRowConfirmation) {
const confirmMessage = tableProperties.removeRowConfirmationMessage || localize('designer.defaultRemoveRowConfirmationMessage', "Are you sure you want to remove the row?");
const result = await this._dialogService.confirm({
type: 'question',
message: confirmMessage
});
if (!result.confirmed) {
return;
}
}
this.handleEdit({ this.handleEdit({
type: DesignerEditType.Remove, type: DesignerEditType.Remove,
path: [...propertyPath, e.row] path: [...propertyPath, e.row]

View File

@@ -74,7 +74,7 @@ export interface DesignerUIState {
activeTabId: PanelTabIdentifier; activeTabId: PanelTabIdentifier;
} }
export type DesignerAction = 'save' | 'initialize' | 'processEdit' | 'generateScript' | 'generateReport'; export type DesignerAction = 'publish' | 'initialize' | 'processEdit' | 'generateScript' | 'generateReport';
export interface DesignerEditProcessedEventArgs { export interface DesignerEditProcessedEventArgs {
result: DesignerEditResult; result: DesignerEditResult;
@@ -154,22 +154,18 @@ export interface DesignerTableProperties extends ComponentProperties {
* the name of the properties to be displayed, properties not in this list will be accessible in details view. * the name of the properties to be displayed, properties not in this list will be accessible in details view.
*/ */
columns?: string[]; columns?: string[];
/** /**
* The display name of the object type. * The display name of the object type.
*/ */
objectTypeDisplayName: string; objectTypeDisplayName: string;
/** /**
* The properties of the table data item. * The properties of the table data item.
*/ */
itemProperties?: DesignerDataPropertyInfo[]; itemProperties?: DesignerDataPropertyInfo[];
/** /**
* The data to be displayed. * The data to be displayed.
*/ */
data?: DesignerTableComponentRowData[]; data?: DesignerTableComponentRowData[];
/** /**
* Whether user can add new rows to the table. The default value is true. * Whether user can add new rows to the table. The default value is true.
*/ */
@@ -179,6 +175,14 @@ export interface DesignerTableProperties extends ComponentProperties {
* Whether user can remove rows from the table. The default value is true. * Whether user can remove rows from the table. The default value is true.
*/ */
canRemoveRows?: boolean; canRemoveRows?: boolean;
/**
* Whether to show confirmation when user removes a row. The default value is false.
*/
showRemoveRowConfirmation?: boolean;
/**
* The confirmation message to be displayed when user removes a row.
*/
removeRowConfirmationMessage?: string;
} }
export interface DesignerTableComponentRowData { export interface DesignerTableComponentRowData {

View File

@@ -26,30 +26,23 @@ export class TableDesignerInput extends EditorInput {
constructor( constructor(
private _provider: TableDesignerProvider, private _provider: TableDesignerProvider,
private _tableInfo: azdata.designers.TableInfo, tableInfo: azdata.designers.TableInfo,
telemetryInfo: { [key: string]: string }, telemetryInfo: { [key: string]: string },
@IInstantiationService private readonly _instantiationService: IInstantiationService, @IInstantiationService private readonly _instantiationService: IInstantiationService,
@IEditorService editorService: IEditorService, @IEditorService private readonly _editorService: IEditorService,
@INotificationService private readonly _notificationService: INotificationService) { @INotificationService private readonly _notificationService: INotificationService) {
super(); super();
this._designerComponentInput = this._instantiationService.createInstance(TableDesignerComponentInput, this._provider, this._tableInfo, telemetryInfo); this._designerComponentInput = this._instantiationService.createInstance(TableDesignerComponentInput, this._provider, tableInfo, telemetryInfo);
this._register(this._designerComponentInput.onStateChange((e) => { this._register(this._designerComponentInput.onStateChange((e) => {
if (e.previousState.pendingAction === 'publish') {
this.setEditorLabel();
this._onDidChangeLabel.fire();
}
if (e.currentState.dirty !== e.previousState.dirty) { if (e.currentState.dirty !== e.previousState.dirty) {
this._onDidChangeDirty.fire(); this._onDidChangeDirty.fire();
} }
})); }));
if (this._tableInfo.isNewTable) { this.setEditorLabel();
const existingNames = editorService.editors.map(editor => editor.getName());
// Find the next available unique name for the new table designer
let idx = 1;
do {
this._name = `${NewTable} ${idx}`;
idx++;
} while (existingNames.indexOf(this._name) !== -1);
} else {
this._name = `${this._tableInfo.schema}.${this._tableInfo.name}`;
}
this._title = `${this._tableInfo.server}.${this._tableInfo.database} - ${this._name}`;
} }
get typeId(): string { get typeId(): string {
@@ -80,7 +73,7 @@ export class TableDesignerInput extends EditorInput {
} }
override isSaving(): boolean { override isSaving(): boolean {
return this._designerComponentInput.pendingAction === 'save'; return this._designerComponentInput.pendingAction === 'publish';
} }
override async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> { override async save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
@@ -99,11 +92,27 @@ export class TableDesignerInput extends EditorInput {
override matches(otherInput: any): boolean { override matches(otherInput: any): boolean {
return otherInput instanceof TableDesignerInput return otherInput instanceof TableDesignerInput
&& this._provider.providerId === otherInput._provider.providerId && this._provider.providerId === otherInput._provider.providerId
&& this._tableInfo.id === otherInput._tableInfo.id; && this._designerComponentInput.tableInfo.id === otherInput._designerComponentInput.tableInfo.id;
} }
override dispose(): void { override dispose(): void {
super.dispose(); super.dispose();
this._provider.disposeTableDesigner(this._tableInfo).then(undefined, err => onUnexpectedError(err)); this._provider.disposeTableDesigner(this._designerComponentInput.tableInfo).then(undefined, err => onUnexpectedError(err));
}
private setEditorLabel(): void {
const tableInfo = this._designerComponentInput.tableInfo;
if (tableInfo.isNewTable) {
const existingNames = this._editorService.editors.map(editor => editor.getName());
// Find the next available unique name for the new table designer
let idx = 1;
do {
this._name = `${NewTable} ${idx}`;
idx++;
} while (existingNames.indexOf(this._name) !== -1);
} else {
this._name = `${tableInfo.schema}.${tableInfo.name}`;
}
this._title = `${tableInfo.server}.${tableInfo.database} - ${this._name}`;
} }
} }

View File

@@ -16,7 +16,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { GenerateTableChangeScriptAction, PublishTableChangesAction } from 'sql/workbench/contrib/tableDesigner/browser/actions'; import { PublishTableChangesAction } from 'sql/workbench/contrib/tableDesigner/browser/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { IColorTheme, ICssStyleCollector, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { DesignerPaneSeparator } from 'sql/platform/theme/common/colorRegistry'; import { DesignerPaneSeparator } from 'sql/platform/theme/common/colorRegistry';
@@ -26,7 +26,6 @@ export class TableDesignerEditor extends EditorPane {
private _designer: Designer; private _designer: Designer;
private _publishChangesAction: PublishTableChangesAction; private _publishChangesAction: PublishTableChangesAction;
private _generateScriptAction: GenerateTableChangeScriptAction;
constructor( constructor(
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
@@ -46,7 +45,6 @@ export class TableDesignerEditor extends EditorPane {
const designerInput = input.getComponentInput(); const designerInput = input.getComponentInput();
this._designer.setInput(designerInput); this._designer.setInput(designerInput);
this._publishChangesAction.setContext(designerInput); this._publishChangesAction.setContext(designerInput);
this._generateScriptAction.setContext(designerInput);
} }
protected createEditor(parent: HTMLElement): void { protected createEditor(parent: HTMLElement): void {
@@ -58,9 +56,7 @@ export class TableDesignerEditor extends EditorPane {
this._register(actionbar); this._register(actionbar);
this._publishChangesAction = this._instantiationService.createInstance(PublishTableChangesAction); this._publishChangesAction = this._instantiationService.createInstance(PublishTableChangesAction);
this._publishChangesAction.enabled = false; this._publishChangesAction.enabled = false;
this._generateScriptAction = this._instantiationService.createInstance(GenerateTableChangeScriptAction); actionbar.push([this._publishChangesAction], { icon: true, label: false });
this._generateScriptAction.enabled = false;
actionbar.push([this._publishChangesAction, this._generateScriptAction], { icon: true, label: false });
this._designer = this._instantiationService.createInstance(Designer, designerContainer); this._designer = this._instantiationService.createInstance(Designer, designerContainer);
this._register(attachDesignerStyler(this._designer, this.themeService)); this._register(attachDesignerStyler(this._designer, this.themeService));

View File

@@ -38,7 +38,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
}; };
constructor(private readonly _provider: TableDesignerProvider, constructor(private readonly _provider: TableDesignerProvider,
private _tableInfo: azdata.designers.TableInfo, public tableInfo: azdata.designers.TableInfo,
private _telemetryInfo: ITelemetryEventProperties, private _telemetryInfo: ITelemetryEventProperties,
@INotificationService private readonly _notificationService: INotificationService, @INotificationService private readonly _notificationService: INotificationService,
@IAdsTelemetryService readonly _adsTelemetryService: IAdsTelemetryService, @IAdsTelemetryService readonly _adsTelemetryService: IAdsTelemetryService,
@@ -77,7 +77,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
this.designerEditTypeDisplayValue[edit.type]).withAdditionalProperties(telemetryInfo); this.designerEditTypeDisplayValue[edit.type]).withAdditionalProperties(telemetryInfo);
const startTime = new Date().getTime(); const startTime = new Date().getTime();
this.updateState(this.valid, this.dirty, 'processEdit'); this.updateState(this.valid, this.dirty, 'processEdit');
this._provider.processTableEdit(this._tableInfo, edit).then( this._provider.processTableEdit(this.tableInfo, edit).then(
result => { result => {
this._viewModel = result.viewModel; this._viewModel = result.viewModel;
this.updateState(result.isValid, !equals(this._viewModel, this._originalViewModel), undefined); this.updateState(result.isValid, !equals(this._viewModel, this._originalViewModel), undefined);
@@ -113,7 +113,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
const startTime = new Date().getTime(); const startTime = new Date().getTime();
try { try {
this.updateState(this.valid, this.dirty, 'generateScript'); this.updateState(this.valid, this.dirty, 'generateScript');
const script = await this._provider.generateScript(this._tableInfo); const script = await this._provider.generateScript(this.tableInfo);
this._queryEditorService.newSqlEditor({ initalContent: script }); this._queryEditorService.newSqlEditor({ initalContent: script });
this.updateState(this.valid, this.dirty); this.updateState(this.valid, this.dirty);
notificationHandle.updateMessage(localize('tableDesigner.generatingScriptCompleted', "Script generated.")); notificationHandle.updateMessage(localize('tableDesigner.generatingScriptCompleted', "Script generated."));
@@ -133,16 +133,17 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
const publishEvent = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.PublishChanges).withAdditionalProperties(telemetryInfo); const publishEvent = this._adsTelemetryService.createActionEvent(TelemetryView.TableDesigner, TelemetryAction.PublishChanges).withAdditionalProperties(telemetryInfo);
const saveNotificationHandle = this._notificationService.notify({ const saveNotificationHandle = this._notificationService.notify({
severity: Severity.Info, severity: Severity.Info,
message: localize('tableDesigner.savingChanges', "Saving table designer changes..."), message: localize('tableDesigner.savingChanges', "Publishing table designer changes..."),
sticky: true sticky: true
}); });
const startTime = new Date().getTime(); const startTime = new Date().getTime();
try { try {
this.updateState(this.valid, this.dirty, 'save'); this.updateState(this.valid, this.dirty, 'publish');
await this._provider.publishChanges(this._tableInfo); const result = await this._provider.publishChanges(this.tableInfo);
this._originalViewModel = this._viewModel; this._originalViewModel = this._viewModel;
this.updateState(true, false);
saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeSuccess', "The changes have been successfully published.")); saveNotificationHandle.updateMessage(localize('tableDesigner.publishChangeSuccess', "The changes have been successfully published."));
this.tableInfo = result.newTableInfo;
this.updateState(true, false);
publishEvent.withAdditionalMeasurements({ publishEvent.withAdditionalMeasurements({
'elapsedTimeMs': new Date().getTime() - startTime 'elapsedTimeMs': new Date().getTime() - startTime
}).send(); }).send();
@@ -164,7 +165,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
let report; let report;
try { try {
this.updateState(this.valid, this.dirty, 'generateReport'); this.updateState(this.valid, this.dirty, 'generateReport');
report = await this._provider.generatePreviewReport(this._tableInfo); report = await this._provider.generatePreviewReport(this.tableInfo);
reportNotificationHandle.close(); reportNotificationHandle.close();
this.updateState(this.valid, this.dirty); this.updateState(this.valid, this.dirty);
} catch (error) { } catch (error) {
@@ -216,7 +217,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
} }
this.updateState(this.valid, this.dirty, 'initialize'); this.updateState(this.valid, this.dirty, 'initialize');
this._provider.initializeTableDesigner(this._tableInfo).then(result => { this._provider.initializeTableDesigner(this.tableInfo).then(result => {
this.doInitialization(result); this.doInitialization(result);
this._onInitialized.fire(); this._onInitialized.fire();
}, error => { }, error => {
@@ -225,9 +226,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
} }
private doInitialization(designerInfo: azdata.designers.TableDesignerInfo): void { private doInitialization(designerInfo: azdata.designers.TableDesignerInfo): void {
this.updateState(true, false); this.updateState(true, this.tableInfo.isNewTable);
this._viewModel = designerInfo.viewModel; this._viewModel = designerInfo.viewModel;
this._originalViewModel = deepClone(this._viewModel); this._originalViewModel = this.tableInfo.isNewTable ? undefined : deepClone(this._viewModel);
this.setDefaultData(); this.setDefaultData();
const tabs = []; const tabs = [];
@@ -391,7 +392,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
itemProperties: this.addAdditionalTableProperties(options, columnProperties), itemProperties: this.addAdditionalTableProperties(options, columnProperties),
objectTypeDisplayName: localize('tableDesigner.columnTypeName', "Column"), objectTypeDisplayName: localize('tableDesigner.columnTypeName', "Column"),
canAddRows: options.canAddRows, canAddRows: options.canAddRows,
canRemoveRows: options.canRemoveRows canRemoveRows: options.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
] ]
@@ -403,17 +406,17 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
const foreignKeyColumnMappingProperties: DesignerDataPropertyInfo[] = [ const foreignKeyColumnMappingProperties: DesignerDataPropertyInfo[] = [
{ {
componentType: 'dropdown', componentType: 'dropdown',
propertyName: designers.ForeignKeyColumnMappingProperty.ForeignKeyColumn, propertyName: designers.ForeignKeyColumnMappingProperty.ForeignColumn,
componentProperties: { componentProperties: {
title: localize('tableDesigner.foreignKeyColumn', "Foreign Key Column"), title: localize('tableDesigner.foreignKey.foreignColumn', "Foreign Column"),
width: 150 width: 150
} }
}, },
{ {
componentType: 'dropdown', componentType: 'dropdown',
propertyName: designers.ForeignKeyColumnMappingProperty.PrimaryKeyColumn, propertyName: designers.ForeignKeyColumnMappingProperty.Column,
componentProperties: { componentProperties: {
title: localize('tableDesigner.primaryKeyColumn', "Primary Key Column"), title: localize('tableDesigner.foreignKey.column', "Column"),
width: 150 width: 150
} }
}, },
@@ -431,10 +434,10 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
}, },
{ {
componentType: 'dropdown', componentType: 'dropdown',
propertyName: designers.TableForeignKeyProperty.PrimaryKeyTable, propertyName: designers.TableForeignKeyProperty.ForeignTable,
description: localize('designer.foreignkey.description.primaryKeyTable', "The table which contains the primary or unique key column."), description: localize('designer.foreignkey.description.primaryKeyTable', "The table which contains the primary or unique key column."),
componentProperties: { componentProperties: {
title: localize('tableDesigner.PrimaryKeyTableName', "Primary Key Table"), title: localize('tableDesigner.ForeignTableName', "Foreign Table"),
width: 200 width: 200
} }
}, },
@@ -460,14 +463,16 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
componentType: 'table', componentType: 'table',
propertyName: designers.TableForeignKeyProperty.Columns, propertyName: designers.TableForeignKeyProperty.Columns,
description: localize('designer.foreignkey.description.columnMapping', "The mapping between foreign key columns and primary key columns."), description: localize('designer.foreignkey.description.columnMapping', "The mapping between foreign key columns and primary key columns."),
group: localize('tableDesigner.foreignKeyColumns', "Column Mapping"), group: localize('tableDesigner.foreignKeyColumns', "Columns"),
componentProperties: <DesignerTableProperties>{ componentProperties: <DesignerTableProperties>{
ariaLabel: localize('tableDesigner.foreignKeyColumns', "Column Mapping"), ariaLabel: localize('tableDesigner.foreignKeyColumns', "Columns"),
columns: [designers.ForeignKeyColumnMappingProperty.ForeignKeyColumn, designers.ForeignKeyColumnMappingProperty.PrimaryKeyColumn], columns: [designers.ForeignKeyColumnMappingProperty.Column, designers.ForeignKeyColumnMappingProperty.ForeignColumn],
itemProperties: foreignKeyColumnMappingProperties, itemProperties: foreignKeyColumnMappingProperties,
objectTypeDisplayName: '', objectTypeDisplayName: '',
canAddRows: options.canAddRows, canAddRows: options.canAddRows,
canRemoveRows: options.canRemoveRows canRemoveRows: options.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
]; ];
@@ -481,11 +486,13 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
showInPropertiesView: false, showInPropertiesView: false,
componentProperties: <DesignerTableProperties>{ componentProperties: <DesignerTableProperties>{
ariaLabel: localize('tableDesigner.foreignKeysTabTitle', "Foreign Keys"), ariaLabel: localize('tableDesigner.foreignKeysTabTitle', "Foreign Keys"),
columns: this.getTableDisplayProperties(options, [designers.TableForeignKeyProperty.Name, designers.TableForeignKeyProperty.PrimaryKeyTable]), columns: this.getTableDisplayProperties(options, [designers.TableForeignKeyProperty.Name, designers.TableForeignKeyProperty.ForeignTable]),
itemProperties: this.addAdditionalTableProperties(options, foreignKeyProperties), itemProperties: this.addAdditionalTableProperties(options, foreignKeyProperties),
objectTypeDisplayName: localize('tableDesigner.ForeignKeyTypeName', "Foreign Key"), objectTypeDisplayName: localize('tableDesigner.ForeignKeyTypeName', "Foreign Key"),
canAddRows: options.canAddRows, canAddRows: options.canAddRows,
canRemoveRows: options.canRemoveRows canRemoveRows: options.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
] ]
@@ -526,7 +533,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
itemProperties: this.addAdditionalTableProperties(options, checkConstraintProperties), itemProperties: this.addAdditionalTableProperties(options, checkConstraintProperties),
objectTypeDisplayName: localize('tableDesigner.checkConstraintTypeName', "Check Constraint"), objectTypeDisplayName: localize('tableDesigner.checkConstraintTypeName', "Check Constraint"),
canAddRows: options.canAddRows, canAddRows: options.canAddRows,
canRemoveRows: options.canRemoveRows canRemoveRows: options.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
] ]
@@ -564,7 +573,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
itemProperties: this.addAdditionalTableProperties(columnSpecTableOptions, columnSpecProperties), itemProperties: this.addAdditionalTableProperties(columnSpecTableOptions, columnSpecProperties),
objectTypeDisplayName: '', objectTypeDisplayName: '',
canAddRows: columnSpecTableOptions.canAddRows, canAddRows: columnSpecTableOptions.canAddRows,
canRemoveRows: columnSpecTableOptions.canRemoveRows canRemoveRows: columnSpecTableOptions.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
]; ];
@@ -582,7 +593,9 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
itemProperties: this.addAdditionalTableProperties(options, indexProperties), itemProperties: this.addAdditionalTableProperties(options, indexProperties),
objectTypeDisplayName: localize('tableDesigner.IndexTypeName', "Index"), objectTypeDisplayName: localize('tableDesigner.IndexTypeName', "Index"),
canAddRows: options.canAddRows, canAddRows: options.canAddRows,
canRemoveRows: options.canRemoveRows canRemoveRows: options.canRemoveRows,
removeRowConfirmationMessage: options.removeRowConfirmationMessage,
showRemoveRowConfirmation: options.showRemoveRowConfirmation
} }
} }
] ]
@@ -616,7 +629,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput {
private createTelemetryInfo(): ITelemetryEventProperties { private createTelemetryInfo(): ITelemetryEventProperties {
let telemetryInfo = { let telemetryInfo = {
provider: this._provider.providerId, provider: this._provider.providerId,
isNewTable: this._tableInfo.isNewTable, isNewTable: this.tableInfo.isNewTable,
}; };
Object.assign(telemetryInfo, this._telemetryInfo); Object.assign(telemetryInfo, this._telemetryInfo);
return telemetryInfo; return telemetryInfo;