initial PR for table designer feature (#17200)

* wip

* wire up e2e

* hook up styler and add as dataprotocal feature

* designer child component rendering

* table component updates

* styler and selectbox column editor

* fix editor size and dupe component creation issue

* fix checkbox column and add more testing data

* properties pane

* only rerender when needed

* properties pane update

* update commands

* cleanup for PR

* revert unwanted changes

* Adding a few tests for Add SQL binding (#17079)

* initial changes

* add a couple more tests

* LEGO: check in for main to temporary branch. (#17089)

* LEGO: check in for main to temporary branch. (#17091)

Co-authored-by: kburtram <karlb@microsoft.com>

* Adds autorest-based SQL Project generation to SQL Database Projects extension (#17078)

* Initial changes

* checkpoint

* Constructing project with post deployment script

* Correcting to intentionally read from cached list of projects

* Adding activation event, fixing fresh workspace bug

* Convert netcoreTool and autorestHelper to share a helper class for streamed command

* Include npm package version to force update

* test checkpoint

* Unit tests

* Added contextual quickpicks for autorest dialogs

* Adding projectController test

* Added projectController test, some refactoring for testability

* Merge branch 'main' into benjin/autorest

* Fixing 'which' import

* PR feedback

* Fixing tests

* Adding additional information for when project provider tests fail

* Hopefully fixing failing tests (unable to repro locally)

* Adding Generate Project item to workspace menu

* PR feedback

* LEGO: check in for main to temporary branch. (#17097)

* added sql database projects strings (#17100)

* Set kernelAlias in startSession when isValidConnection is truthy (#17102)

* PR follow-up comments (#17113)

* Change recompare message after changing options to modal (#17103)

* Change recompare message to modal

* change options to yes and no

* Remove commented code block in git extension (#17116)

* Remove commented code block in git extension

* Add SQL CARBON EDIT tag

* [Loc] Small change to generatingProjectFailed (#17118)

* Add Null Shortcut and added NULL text for default NULL value. (#17085)

* added test key event

* added null function to tryHandleKeyEvent

* added null formatting

* added working null insert.

* added editDataGridPanel string null support

* Bump nth-check from 2.0.0 to 2.0.1 in /build (#17115)

Bumps [nth-check](https://github.com/fb55/nth-check) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add excludeFlags to extenson marketplace query (#17121)

* Add excludeFlags to extenson marketplace query

* Remove dead code

* Remove extraneous blank line

* Address code review feedback

* Adding Derived Columns to ADS Flatfile Import (#16795)

* Adding derived column boilerplate

* brandan preliminary frontend changes

* empty commit

* added new param

* updating contracts, dialogue changes

* utils changes, saving timeout attempt

* pushing for aasim

* Cleaning up code and fixing the issue in theory

* changing button, did not solve independent scroll

* Fixing the scroll bar issue

* updating flat file service

* adding override keyword to overrriden method

* improving UI

* pushing changes associated with resolved comments

* localizing strings, editing comments

* all comments resolved

* Fixing a test

* updating import package
Updating azure MFA bug

* Clearing navigation validator
Fixing broken table name change

* fixed prose test

* removing unused code from tests

* Fixed PR comments

* Fixing some PR comments

* WIP

* Fixing transformation code and create derived column dialog styling

* removing unused code

* Adding comment for console log

* fixed table styling

* Adding some aria labels

* Fixed some code cleanup issues

* update import service

Co-authored-by: Aasim Khan <aasimkhan30@gmail.com>
Co-authored-by: bnhoule <t-bhoule@microsoft.com>

* Change keybindings for copying query with Results (#17127)

Co-authored-by: Monica Gupta <mogupt@microsoft.com>

* sql proj - publish to docker improvements  (#17124)

* Add AAD token expiration handling for query runner (#17117)

* Add AAD token refresh for notebook

* move token refresh to query management and remove previous refresh calls

* Add guids to RunAll and RunCell events (#17123)

Add guids to RunAll and RunCell events

* add error banner for failed migration cutover and cancel migration (#17106)

* [Loc] update to sql migration, database projects and import extension strings (#17130)

* Apply optional storage class settings in sql mi create command (#17129)

* Make storage classes optional

* Fix notebook to use storage class options

Co-authored-by: Charmaine Chan <chachan@microsoft.com>

* Add support for adding new setting in local.settings.json in add SQL binding quickpick (#17093)

* be able to add new setting in local.settings.json

* cleanup

* addressing comments

* remove todo comment

* addressing comments

* update some strings to uris

* bump version of sqltoolsservice (#17133)

* mark schema compare tests as unstable (#17140)

* [Loc] Update for arc and sql-database-projects (#17148)

* ML extension vbump (#17143)

* Configure docker image and web smoke tests for ADSWeb. (#17136)

* Adjusts timeout period

* Revert "Adjusts timeout period"

This reverts commit 0f372eae2a4611554093b6c09f1ff6c451132e19.

* Adds firefox as browser option

* Corrects yaml smoke test script

* Resets args array to original values

* Corrects build path

* Resolves ignoring browser option error

* continue even after writing to stderr

* Adjusts smoke test (browser) script

* More adjustments to smoke test script

* Corrects server path

* Uses build variable directly in build path

* Specifies browser type since cannot be ignored error

* Adds browser option

* Updates web build image and corrects smoke test exe command

* Removes commented out task

* Updates dockerfile to support web smoketests

* Removes failOnStderr flag

* Use curl instead of wget in Dockerfile

* Fixed a bug with cancling publish (#17160)

* Save And Close Functionality (#17000)

* save and close

* wip

* working save and close

* cleanup

* pr changes

* pr changes

* fix capitalization

* fix build

* pr fix

* Added dynamic options for SQL MIAA Deployment Wizard and updated checkbox field (#17119)

* Dynamic enablement

* Added new package.json field for dynamic options and corresponding functions and classes.

* Enabled dynamic options non-generalized and changed formatting of checkbox to have label on the left.

* Added setOptions under InputComponentInfo for generalization, comments under checkbox component, and changed Replicas to High Availability to reflect parity in portal.

* fix unit test

Co-authored-by: Candice Ye <canye@microsoft.com>
Co-authored-by: Alan Ren <alanren@microsoft.com>

* LEGO: check in for main to temporary branch. (#17168)

* [Loc] added new arc strings and fix for sql-database-projects lcl file for Japanese (#17171)

* [Loc] added new arc strings and fix for sql-database-projects xlf

* removed newline

* Port of Vscode fix for colors too close (#17146)

* default light colors list.focusHighlightForeground too close to list.activeSelectionBackground. Fixes #127597

* remove activeSelectionBackground from theme-carbon as it conflicts with vscode.

* remove dark carbon background

Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>

* Markdown Horizontal Scrollbar Fix (#17083)

* dynamically change horizontal scrollbar

* working horizontal scrollbar

* created new event to handle both scrollbar and mouse wheel

* only show scrollbar when needed

* LEGO: check in for main to temporary branch. (#17181)

* Bump axios to 0.21.4 (#17161)

* Kusto version bump to 0.5.6 (#17114)

* Bumped Kusto toolsservice version to 125 and bumped version to 0.5.6

* Changed netcoreapp3.1 to net5.0 in Kusto config.json

* AzureMonitor bump ToolService version and extension version. (#17174)

* Bump concat-with-sourcemaps from 1.0.4 to 1.1.0 (#17158)

Bumps [concat-with-sourcemaps](https://github.com/floridoo/concat-with-sourcemaps) from 1.0.4 to 1.1.0.
- [Release notes](https://github.com/floridoo/concat-with-sourcemaps/releases)
- [Commits](https://github.com/floridoo/concat-with-sourcemaps/commits)

---
updated-dependencies:
- dependency-name: concat-with-sourcemaps
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update service downloader to 0.2.3 (#17186)

* Notebook Views grid fixes (#17170)

* update data workspace restart ADS to open workspace message (#17188)

* update message

* update string

* Add back "Remove Project" (#17178)

* remove project working with full paths

* use relative paths

* const

* addressing comments

* Bump github-auth axios to 0.21.4 (#17189)

* LEGO: check in for main to temporary branch. (#17192)

* LEGO: check in for main to temporary branch. (#17190)

Co-authored-by: kburtram <karlb@microsoft.com>

* [Loc] Add a small change to dataworkspace (#17194)

* added bump to sqltoolsservice version (#17195)

* Check if file is dirty before adding sql binding (#17175)

* check if file is dirty before adding sql binding

* Addressing comments

* Add vertical scroll bar to Preview in Split View (#17164)

* reset max height

* add editor height

* set md editor height

* Split up NotebookProvider into separate providers for handling file serialization and cell execution. (#17176)

* fix floating promises

* pr comments

* reuse component definition

* comments

* fix error

Co-authored-by: Kim Santiago <31145923+kisantia@users.noreply.github.com>
Co-authored-by: csigs <csigs@users.noreply.github.com>
Co-authored-by: kburtram <karlb@microsoft.com>
Co-authored-by: Benjin Dubishar <benjin.dubishar@gmail.com>
Co-authored-by: Alex Ma <alma1@microsoft.com>
Co-authored-by: Justin M <63619224+JustinMDotNet@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: bnhoule <52506119+bnhoule@users.noreply.github.com>
Co-authored-by: Aasim Khan <aasimkhan30@gmail.com>
Co-authored-by: bnhoule <t-bhoule@microsoft.com>
Co-authored-by: Monica Gupta <scorpio90m@gmail.com>
Co-authored-by: Monica Gupta <mogupt@microsoft.com>
Co-authored-by: Leila Lali <llali@microsoft.com>
Co-authored-by: Hai Cao <hacao@microsoft.com>
Co-authored-by: Daniel Grajeda <dagrajed@microsoft.com>
Co-authored-by: brian-harris <61598682+brian-harris@users.noreply.github.com>
Co-authored-by: Charmaine Chan <69230572+charmainewkchan@users.noreply.github.com>
Co-authored-by: Charmaine Chan <chachan@microsoft.com>
Co-authored-by: Lewis Sanchez <87730006+lewis-sanchez@users.noreply.github.com>
Co-authored-by: Christopher Suh <chsuh@microsoft.com>
Co-authored-by: Candice Ye <candiceye@berkeley.edu>
Co-authored-by: Candice Ye <canye@microsoft.com>
Co-authored-by: Martin Aeschlimann <martinae@microsoft.com>
Co-authored-by: Vasu Bhog <vabhog@microsoft.com>
Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
Co-authored-by: Barbara Valdez <34872381+barbaravaldez@users.noreply.github.com>
Co-authored-by: Cory Rivera <corivera@microsoft.com>
This commit is contained in:
Alan Ren
2021-10-08 19:24:31 -07:00
committed by GitHub
co-authored by dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> kburtram Kim Santiago csigs Benjin Dubishar Alex Ma Justin M bnhoule Aasim Khan bnhoule Monica Gupta Monica Gupta Leila Lali Hai Cao Daniel Grajeda brian-harris Charmaine Chan Charmaine Chan Lewis Sanchez Christopher Suh Candice Ye Candice Ye Martin Aeschlimann Vasu Bhog Charles Gagnon Barbara Valdez Cory Rivera
parent 8b1fa3ea52
commit abe54a2b92
32 changed files with 2197 additions and 166 deletions
+267
View File
@@ -988,4 +988,271 @@ declare module 'azdata' {
*/
unsupportedVersionMessage?: string;
}
export enum DataProviderType {
TableDesignerProvider = 'TableDesignerProvider'
}
export namespace dataprotocol {
export function registerTableDesignerProvider(provider: designers.TableDesignerProvider): vscode.Disposable;
}
export namespace designers {
/**
* Open a table designer window.
* @param providerId The table designer provider Id.
* @param tableInfo The table information. The object will be passed back to the table designer provider as the unique identifier for the table.
*/
export function openTableDesigner(providerId: string, tableInfo: TableInfo): Thenable<void>;
/**
* Definition for the table designer provider.
*/
export interface TableDesignerProvider extends DataProvider {
/**
* Gets the table designer information for the specified table.
* @param table the table information.
*/
getTableDesignerInfo(table: TableInfo): Thenable<TableDesignerInfo>;
/**
*
* @param table the table information
* @param data the object contains the state of the table designer
* @param tableChangeInfo the information about the change user made through the UI.
*/
processTableEdit(table: TableInfo, data: DesignerData, tableChangeInfo: DesignerEdit): Thenable<DesignerEditResult>;
}
/**
* The information of the table.
*/
export interface TableInfo {
/**
* The server name.
*/
server: string;
/**
* The database name
*/
database: string;
/**
* The schema name, only required for existing table.
*/
schema?: string;
/**
* The table name, only required for existing table.
*/
name?: string;
/**
* A boolean value indicates whether a new table is being designed.
*/
isNewTable: boolean;
/**
* Extension can store additional information that the provider needs to uniquely identify a table.
*/
[key: string]: any;
}
/**
* The information to populate the table designer UI.
*/
export interface TableDesignerInfo {
/**
* The view definition.
*/
view: TableDesignerView;
/**
* The data model.
*/
data: DesignerData;
/**
* The supported column types
*/
columnTypes: string[];
/**
* The list of schemas in the database.
*/
schemas: string[];
}
/**
* Name of the common table properties.
* Extensions can use the names to access the designer data.
*/
export enum TableProperty {
Columns = 'columns',
Description = 'description',
Name = 'name',
Schema = 'schema',
Script = 'script'
}
/**
* Name of the common table column properties.
* Extensions can use the names to access the designer data.
*/
export enum TableColumnProperty {
AllowNulls = 'allowNulls',
DefaultValue = 'defaultValue',
Length = 'length',
Name = 'name',
Type = 'type'
}
/**
* The table designer view definition
*/
export interface TableDesignerView {
/**
* Additional table properties. Common table properties are handled by Azure Data Studio. see {@link TableProperty}
*/
additionalTableProperties?: DesignerDataPropertyInfo[];
/**
* Additional table column properties.Common table properties are handled by Azure Data Studio. see {@link TableColumnProperty}
*/
addtionalTableColumnProperties?: DesignerDataPropertyInfo[];
/**
* Additional tabs.
*/
addtionalTabs?: DesignerTab[];
}
/**
* The data model object of the designer.
*/
export interface DesignerData {
[key: string]: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
/**
* The definition of a designer tab
*/
export interface DesignerTab {
/**
* The title of the tab
*/
title: string;
/**
* the components to be displayed in this tab.
*/
components: DesignerDataPropertyInfo[];
}
/**
* The definition of the property in the designer.
*/
export interface DesignerDataPropertyInfo {
/**
* The property name
*/
propertyName: string;
/**
* The component type
*/
componentType: DesignerComponentTypeName;
/**
* The group name, properties with the same group name will be displayed under the same group on the UI.
*/
group?: string;
/**
* The properties of the component.
*/
componentProperties: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
/**
* The child component types supported by designer.
*/
export type DesignerComponentTypeName = 'input' | 'checkbox' | 'dropdown' | 'table';
/**
* 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[];
}
/**
* The data item of the designer's table component.
*/
export interface DesignerTableComponentDataItem {
[key: string]: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
/**
* Type of the edit originated from the designer UI.
*/
export enum DesignerEditType {
/**
* Add a row to a table
*/
Add = 0,
/**
* Remove a row from a table
*/
Remove = 1,
/**
* Update a property
*/
Update = 2
}
/**
* Information of the edit originated from the designer UI.
*/
export interface DesignerEdit {
/**
* The edit type
*/
type: DesignerEditType;
/**
* the property that was edited
*/
property: DesignerEditIdentifier;
/**
* the new value
*/
value: any;
}
/**
* The identifier of a property. The value is string typed if the property belongs to the root object, otherwise the type of the value is an object.
*/
export type DesignerEditIdentifier = string | { parentProperty: string, index: number, property: string };
/**
* The result returned by the table designer provider after handling an edit request.
*/
export interface DesignerEditResult {
/**
* The data model object.
*/
data: DesignerData;
/**
* Whether the current state is valid.
*/
isValid: boolean;
/**
* Error messages of current state, and the property the caused the error.
*/
errors?: { message: string, property?: DesignerEditIdentifier }[];
}
}
}
@@ -0,0 +1,480 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DesignerComponentInput, DesignerEditType, DesignerTab, DesignerEdit, DesignerEditIdentifier, DesignerData, DesignerDataPropertyInfo, DesignerTableComponentRowData, DesignerTableProperties, InputBoxProperties, DropDownProperties, CheckBoxProperties, DesignerComponentTypeName } from 'sql/base/browser/ui/designer/interfaces';
import { IPanelTab, ITabbedPanelStyles, TabbedPanel } from 'sql/base/browser/ui/panel/panel';
import * as DOM from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInputBoxStyles, InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import 'vs/css!./media/designer';
import { ITableStyles } from 'sql/base/browser/ui/table/interfaces';
import { IThemable } from 'vs/base/common/styler';
import { Checkbox, ICheckboxStyles } from 'sql/base/browser/ui/checkbox/checkbox';
import { Table } from 'sql/base/browser/ui/table/table';
import { ISelectBoxStyles, SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { localize } from 'vs/nls';
import { TableCellEditorFactory } from 'sql/base/browser/ui/table/tableCellEditorFactory';
import { CheckBoxColumn } from 'sql/base/browser/ui/table/plugins/checkboxColumn.plugin';
import { DesignerTabPanelView } from 'sql/base/browser/ui/designer/designerTabPanelView';
import { DesignerPropertiesPane, PropertiesPaneObjectContext } from 'sql/base/browser/ui/designer/designerPropertiesPane';
export interface IDesignerStyle {
tabbedPanelStyles?: ITabbedPanelStyles;
inputBoxStyles?: IInputBoxStyles;
tableStyles?: ITableStyles;
selectBoxStyles?: ISelectBoxStyles;
checkboxStyles?: ICheckboxStyles;
}
export type DesignerUIComponent = InputBox | Checkbox | Table<Slick.SlickData> | SelectBox;
export type CreateComponentFunc = (container: HTMLElement, component: DesignerDataPropertyInfo, editIdentifier: DesignerEditIdentifier) => DesignerUIComponent;
export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerData) => void;
export class Designer extends Disposable implements IThemable {
private _horizontalSplitViewContainer: HTMLElement;
private _verticalSplitViewContainer: HTMLElement;
private _tabbedPanelContainer: HTMLElement;
private _editorContainer: HTMLElement;
private _horizontalSplitView: SplitView;
private _verticalSplitView: SplitView;
private _tabbedPanel: TabbedPanel;
private _contentContainer: HTMLElement;
private _topContentContainer: HTMLElement;
private _propertiesPaneContainer: HTMLElement;
private _styles: IDesignerStyle = {};
private _supressEditProcessing: boolean = false;
private _componentMap = new Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>();
private _input: DesignerComponentInput;
private _tableCellEditorFactory: TableCellEditorFactory;
private _propertiesPane: DesignerPropertiesPane;
constructor(private readonly _container: HTMLElement,
private readonly _contextViewProvider: IContextViewProvider) {
super();
this._tableCellEditorFactory = new TableCellEditorFactory(
{
valueGetter: (item, column): string => {
return item[column.field].value;
},
valueSetter: async (context: string, row: number, item: DesignerTableComponentRowData, column: Slick.Column<Slick.SlickData>, value: string): Promise<void> => {
await this.handleEdit({
type: DesignerEditType.Update,
property: {
parentProperty: context,
index: row,
property: column.field
},
value: value
});
},
optionsGetter: (item, column): string[] => {
return item[column.field].options;
},
editorStyler: (component) => {
this.styleComponent(component);
}
}, this._contextViewProvider
);
this._verticalSplitViewContainer = DOM.$('.designer-component');
this._horizontalSplitViewContainer = DOM.$('.container');
this._contentContainer = DOM.$('.content-container');
this._topContentContainer = DOM.$('.top-content-container.components-grid');
this._tabbedPanelContainer = DOM.$('.tabbed-panel-container');
this._editorContainer = DOM.$('.editor-container');
this._propertiesPaneContainer = DOM.$('.properties-container');
this._verticalSplitView = new SplitView(this._verticalSplitViewContainer, { orientation: Orientation.VERTICAL });
this._horizontalSplitView = new SplitView(this._horizontalSplitViewContainer, { orientation: Orientation.HORIZONTAL });
this._tabbedPanel = new TabbedPanel(this._tabbedPanelContainer);
this._container.appendChild(this._verticalSplitViewContainer);
this._contentContainer.appendChild(this._topContentContainer);
this._contentContainer.appendChild(this._tabbedPanelContainer);
this._verticalSplitView.addView({
element: this._horizontalSplitViewContainer,
layout: size => {
this.layoutTabbedPanel();
},
minimumSize: 200,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this._verticalSplitView.addView({
element: this._editorContainer,
layout: size => { },
minimumSize: 100,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this._horizontalSplitView.addView({
element: this._contentContainer,
layout: size => {
this.layoutTabbedPanel();
},
minimumSize: 200,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this._horizontalSplitView.addView({
element: this._propertiesPaneContainer,
layout: size => { },
minimumSize: 200,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None
}, Sizing.Distribute);
this._propertiesPane = new DesignerPropertiesPane(this._propertiesPaneContainer, (container, component, identifier) => {
return this.createComponent(container, component, identifier, false, false);
}, (definition, component, data) => {
this.setComponentValue(definition, component, data);
}, (component) => {
this.styleComponent(component);
});
const editor = DOM.$('div');
editor.innerText = 'script pane placeholder';
this._editorContainer.appendChild(editor);
}
private styleComponent(component: TabbedPanel | InputBox | Checkbox | Table<Slick.SlickData> | SelectBox): void {
if (component instanceof InputBox) {
component.style(this._styles.inputBoxStyles);
} else if (component instanceof Checkbox) {
component.style(this._styles.checkboxStyles);
} else if (component instanceof TabbedPanel) {
component.style(this._styles.tabbedPanelStyles);
} else if (component instanceof Table) {
component.style(this._styles.tableStyles);
} else {
component.style(this._styles.selectBoxStyles);
}
}
public style(styles: IDesignerStyle): void {
this._styles = styles;
this._componentMap.forEach((value, key, map) => {
if (value.component.style) {
this.styleComponent(value.component);
}
});
this._propertiesPane.style();
this._verticalSplitView.style({
separatorBorder: styles.selectBoxStyles.selectBorder
});
this._horizontalSplitView.style({
separatorBorder: styles.selectBoxStyles.selectBorder
});
}
public layout(dimension: DOM.Dimension) {
this._verticalSplitView.layout(dimension.height);
this._horizontalSplitView.layout(dimension.width);
}
public async setInput(input: DesignerComponentInput): Promise<void> {
this._input = input;
await this.initializeDesignerView();
}
private async initializeDesignerView(): Promise<void> {
this._propertiesPane.clear();
DOM.clearNode(this._topContentContainer);
const view = await this._input.getView();
if (view.components) {
view.components.forEach(component => {
this.createComponent(this._topContentContainer, component, component.propertyName, true, true);
});
}
this._tabbedPanel.clearTabs();
view.tabs.forEach(tab => {
this._tabbedPanel.pushTab(this.createTabView(tab));
});
this.layoutTabbedPanel();
await this.updateComponentValues();
}
private layoutTabbedPanel() {
this._tabbedPanel.layout(new DOM.Dimension(this._tabbedPanelContainer.clientWidth, this._tabbedPanelContainer.clientHeight));
}
private async updateComponentValues(): Promise<void> {
const data = await this._input.getData();
// data[ScriptPropertyName] -- todo- set the script editor
this._componentMap.forEach((value) => {
this.setComponentValue(value.defintion, value.component, data);
});
let type: string;
let components: DesignerDataPropertyInfo[];
let inputData: DesignerData;
let context: PropertiesPaneObjectContext;
const currentContext = this._propertiesPane.context;
if (currentContext === 'root' || currentContext === undefined) {
context = 'root';
components = [];
this._componentMap.forEach(value => {
components.push(value.defintion);
});
type = this._input.objectTypeDisplayName;
inputData = data;
} else {
context = currentContext;
const tableData = data[currentContext.parentProperty] as DesignerTableProperties;
const tableProperties = this._componentMap.get(currentContext.parentProperty).defintion.componentProperties as DesignerTableProperties;
inputData = tableData.data[currentContext.index] as DesignerData;
components = tableProperties.itemProperties;
type = tableProperties.objectTypeDisplayName;
}
this._propertiesPane.show({
context: context,
type: type,
components: components,
data: inputData
});
}
private async handleEdit(edit: DesignerEdit): Promise<void> {
if (this._supressEditProcessing) {
return;
}
await this.applyEdit(edit);
const result = await this._input.processEdit(edit);
if (result.isValid) {
this._supressEditProcessing = true;
await this.updateComponentValues();
this._supressEditProcessing = false;
} else {
//TODO: add error notification
}
}
private async applyEdit(edit: DesignerEdit): Promise<void> {
const data = await this._input.getData();
switch (edit.type) {
case DesignerEditType.Update:
if (typeof edit.property === 'string') {
// if the type of the property is string then the property is a top level property
const componentData = data[edit.property];
const componentType = this._componentMap.get(edit.property).defintion.componentType;
this.setComponentData(componentType, componentData, edit.value);
} else {
const columnPropertyName = edit.property.property;
const tableInfo = this._componentMap.get(edit.property.parentProperty).defintion.componentProperties as DesignerTableProperties;
const tableProperties = data[edit.property.parentProperty] as DesignerTableProperties;
const componentData = tableProperties.data[edit.property.index][columnPropertyName];
const itemProperty = tableInfo.itemProperties.find(property => property.propertyName === columnPropertyName);
if (itemProperty) {
this.setComponentData(itemProperty.componentType, componentData, edit.value);
}
}
break;
default:
break;
}
}
private setComponentData(componentType: DesignerComponentTypeName, componentData: any, value: any): void {
switch (componentType) {
case 'checkbox':
(<CheckBoxProperties>componentData).checked = value;
break;
case 'dropdown':
(<DropDownProperties>componentData).value = value;
break;
case 'input':
(<InputBoxProperties>componentData).value = value;
break;
}
}
private createTabView(tab: DesignerTab): IPanelTab {
const view = new DesignerTabPanelView(tab, (container, component, identifier) => {
return this.createComponent(container, component, identifier, true, false);
});
return {
identifier: tab.title,
title: tab.title,
view: view
};
}
private setComponentValue(definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerData): void {
this._supressEditProcessing = true;
switch (definition.componentType) {
case 'input':
const input = component as InputBox;
const inputData = data[definition.propertyName] as InputBoxProperties;
input.setEnabled(inputData.enabled ?? true);
input.value = inputData.value?.toString() ?? '';
break;
case 'table':
const table = component as Table<Slick.SlickData>;
const tableDataView = table.getData() as TableDataView<Slick.SlickData>;
tableDataView.clear();
tableDataView.push((data[definition.propertyName] as DesignerTableProperties).data);
table.rerenderGrid();
break;
case 'checkbox':
const checkbox = component as Checkbox;
const checkboxData = data[definition.propertyName] as CheckBoxProperties;
if (checkboxData.enabled === false) {
checkbox.disable();
} else {
checkbox.enable();
}
checkbox.checked = checkboxData.checked;
break;
case 'dropdown':
const dropdown = component as SelectBox;
const defaultDropdownData = definition.componentProperties as DropDownProperties;
const dropdownData = data[definition.propertyName] as DropDownProperties;
if (dropdownData.enabled === false) {
dropdown.disable();
} else {
dropdown.enable();
}
const options = (dropdownData.values || defaultDropdownData.values || []) as string[];
dropdown.setOptions(options);
const idx = options?.indexOf(dropdownData.value as string);
if (idx > -1) {
dropdown.select(idx);
}
break;
default:
break;
}
this._supressEditProcessing = false;
}
private createComponent(container: HTMLElement, componentDefinition: DesignerDataPropertyInfo, editIdentifier: DesignerEditIdentifier, addToComponentMap: boolean, setWidth: boolean): DesignerUIComponent {
const componentContainerClass = componentDefinition.componentType === 'table' ? '.full-row' : '';
const labelContainer = container.appendChild(DOM.$(componentContainerClass));
labelContainer.appendChild(DOM.$('span.component-label')).innerText = (componentDefinition.componentType === 'checkbox' || componentDefinition.componentProperties?.title === undefined) ? '' : componentDefinition.componentProperties.title;
const componentDiv = container.appendChild(DOM.$(componentContainerClass));
let component: DesignerUIComponent;
switch (componentDefinition.componentType) {
case 'input':
const inputProperties = componentDefinition.componentProperties as InputBoxProperties;
const input = new InputBox(componentDiv, this._contextViewProvider, {
ariaLabel: inputProperties.title,
type: inputProperties.inputType,
});
input.onDidChange(async (newValue) => {
await this.handleEdit({ type: DesignerEditType.Update, property: editIdentifier, value: newValue });
});
if (setWidth && inputProperties.width !== undefined) {
input.width = inputProperties.width as number;
}
component = input;
break;
case 'dropdown':
const dropdownProperties = componentDefinition.componentProperties as DropDownProperties;
const dropdown = new SelectBox(dropdownProperties.values as string[], undefined, this._contextViewProvider, undefined);
dropdown.render(componentDiv);
dropdown.selectElem.style.height = '25px';
dropdown.onDidSelect(async (e) => {
await this.handleEdit({ type: DesignerEditType.Update, property: editIdentifier, value: e.selected });
});
component = dropdown;
break;
case 'checkbox':
const checkboxProperties = componentDefinition.componentProperties as CheckBoxProperties;
const checkbox = new Checkbox(componentDiv, {
label: checkboxProperties.title
});
checkbox.onChange(async (newValue) => {
await this.handleEdit({ type: DesignerEditType.Update, property: editIdentifier, value: newValue });
});
component = checkbox;
break;
case 'table':
const tableProperties = componentDefinition.componentProperties as DesignerTableProperties;
const table = new Table(componentDiv, {
dataProvider: new TableDataView()
}, {
editable: true,
autoEdit: true,
dataItemColumnValueExtractor: (data: any, column: Slick.Column<Slick.SlickData>): string => {
return data[column.field].value;
}
}
);
table.columns = tableProperties.columns.map(propName => {
const propertyDefinition = tableProperties.itemProperties.find(item => item.propertyName === propName);
switch (propertyDefinition.componentType) {
case 'checkbox':
const checkboxColumn = new CheckBoxColumn({
field: propertyDefinition.propertyName,
name: propertyDefinition.componentProperties.title,
width: propertyDefinition.componentProperties.width as number
});
table.registerPlugin(checkboxColumn);
checkboxColumn.onChange(async (e) => {
await this.handleEdit({
type: DesignerEditType.Update,
property: {
parentProperty: componentDefinition.propertyName,
index: e.row,
property: propertyDefinition.propertyName
},
value: e.value
});
});
return checkboxColumn.definition;
case 'dropdown':
const dropdownProperties = propertyDefinition.componentProperties as DropDownProperties;
return {
name: dropdownProperties.title,
field: propertyDefinition.propertyName,
editor: this._tableCellEditorFactory.getSelectBoxEditorClass(componentDefinition.propertyName, dropdownProperties.values as string[]),
width: dropdownProperties.width as number
};
default:
const inputProperties = propertyDefinition.componentProperties as InputBoxProperties;
return {
name: inputProperties.title,
field: propertyDefinition.propertyName,
editor: this._tableCellEditorFactory.getTextEditorClass(componentDefinition.propertyName, inputProperties.inputType),
width: inputProperties.width as number
};
}
});
table.layout(new DOM.Dimension(container.clientWidth, container.clientHeight));
table.grid.onBeforeEditCell.subscribe((e, data): boolean => {
return data.item[data.column.field].enabled !== false;
});
table.grid.onActiveCellChanged.subscribe((e, data) => {
this._propertiesPane.show({
context: {
parentProperty: componentDefinition.propertyName,
index: data.row
},
type: tableProperties.objectTypeDisplayName,
components: tableProperties.itemProperties,
data: table.getData().getItem(data.row)
});
});
component = table;
break;
default:
throw new Error(localize('tableDesigner.unknownComponentType', "The component type: {0} is not supported", componentDefinition.componentType));
}
if (addToComponentMap) {
this._componentMap.set(componentDefinition.propertyName, {
defintion: componentDefinition,
component: component
});
}
this.styleComponent(component);
return component;
}
}
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CreateComponentFunc, DesignerUIComponent, SetComponentValueFunc } from 'sql/base/browser/ui/designer/designer';
import { DesignerData, DesignerEditIdentifier, DesignerDataPropertyInfo, InputBoxProperties, NameProperty } from 'sql/base/browser/ui/designer/interfaces';
import * as DOM from 'vs/base/browser/dom';
import { equals } from 'vs/base/common/objects';
import { localize } from 'vs/nls';
export type PropertiesPaneObjectContext = 'root' | {
parentProperty: string;
index: number;
};
export interface ObjectInfo {
context: PropertiesPaneObjectContext;
type: string;
components: DesignerDataPropertyInfo[];
data: DesignerData;
}
export class DesignerPropertiesPane {
private _titleElement: HTMLElement;
private _contentElement: HTMLElement;
private _currentContext?: PropertiesPaneObjectContext;
private _componentMap = new Map<string, { defintion: DesignerDataPropertyInfo, component: DesignerUIComponent }>();
constructor(container: HTMLElement, private _createComponent: CreateComponentFunc, private _setComponentValue: SetComponentValueFunc, private _styleComponent: (component: DesignerUIComponent) => void) {
const titleContainer = container.appendChild(DOM.$('.title-container'));
this._titleElement = titleContainer.appendChild(DOM.$('div'));
this._contentElement = container.appendChild(DOM.$('.properties-content.components-grid'));
this._titleElement.innerText = localize('tableDesigner.propertiesPaneTitle', "Properties");
}
public get context(): PropertiesPaneObjectContext | undefined {
return this._currentContext;
}
public clear(): void {
this._componentMap.forEach((value) => {
value.component.dispose();
});
this._componentMap.clear();
DOM.clearNode(this._contentElement);
this._currentContext = undefined;
}
public style() {
this._componentMap.forEach((value) => {
this._styleComponent(value.component);
});
}
public show(item: ObjectInfo): void {
if (!equals(item.context, this._currentContext)) {
this.clear();
this._currentContext = item.context;
item.components.forEach((value) => {
// todo: handle table type in properties pane
if (value.componentType !== 'table') {
const editIdentifier: DesignerEditIdentifier = this._currentContext === 'root' ? value.propertyName : {
parentProperty: this._currentContext.parentProperty,
index: this._currentContext.index,
property: value.propertyName
};
const component = this._createComponent(this._contentElement, value, editIdentifier);
this._componentMap.set(value.propertyName, {
component: component,
defintion: value
});
}
});
}
const name = (<InputBoxProperties>item.data[NameProperty])?.value ?? '';
this._titleElement.innerText = localize({
key: 'tableDesigner.propertiesPaneTitleWithContext',
comment: ['{0} is the place holder for object type', '{1} is the place holder for object name']
}, "Properties - {0} {1}", item.type, name);
this._componentMap.forEach((value) => {
this._setComponentValue(value.defintion, value.component, item.data);
});
}
}
@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DesignerTab } from 'sql/base/browser/ui/designer/interfaces';
import { IPanelView } from 'sql/base/browser/ui/panel/panel';
import { Table } from 'sql/base/browser/ui/table/table';
import { Disposable } from 'vs/base/common/lifecycle';
import * as DOM from 'vs/base/browser/dom';
import { CreateComponentFunc } from 'sql/base/browser/ui/designer/designer';
export class DesignerTabPanelView extends Disposable implements IPanelView {
private _componentsContainer: HTMLElement;
private _tables: Table<Slick.SlickData>[] = [];
constructor(private readonly _tab: DesignerTab, private _createComponent: CreateComponentFunc) {
super();
this._componentsContainer = DOM.$('.components-grid');
this._tab.components.forEach(componentDefition => {
const component = this._createComponent(this._componentsContainer, componentDefition, componentDefition.propertyName);
if (componentDefition.componentType === 'table') {
this._tables.push(component as Table<Slick.SlickData>);
}
});
}
render(container: HTMLElement): void {
container.appendChild(this._componentsContainer);
}
layout(dimension: DOM.Dimension): void {
this._tables.forEach(table => {
table.layout(new DOM.Dimension(dimension.width - 10, dimension.height - 20));
});
}
}
@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface DesignerComponentInput {
/**
* Gets the object type display name.
*/
readonly objectTypeDisplayName: string;
/**
* Gets the designer view specification.
*/
getView(): Promise<DesignerView>;
/**
* Gets the data.
*/
getData(): Promise<DesignerData>;
/**
* Process the edit made in the designer.
* @param edit the information about the edit.
*/
processEdit(edit: DesignerEdit): Promise<DesignerEditResult>;
}
export const NameProperty = 'name';
export const ScriptProperty = 'script';
export interface DesignerView {
components?: DesignerDataPropertyInfo[]
tabs: DesignerTab[];
}
export interface DesignerTab {
title: string;
components: DesignerDataPropertyInfo[];
}
export interface DesignerData {
[key: string]: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
export interface DesignerDataPropertyInfo {
propertyName: string;
componentType: DesignerComponentTypeName;
group?: string;
componentProperties?: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
export type DesignerComponentTypeName = 'input' | 'checkbox' | 'dropdown' | 'table';
export interface ComponentProperties {
title?: string;
ariaLabel?: string;
width?: number | string;
enabled?: boolean;
}
export interface CategoryValue {
displayName: string;
name: string;
}
export interface DropDownProperties extends ComponentProperties {
value?: string | CategoryValue;
values?: string[] | CategoryValue[];
}
export interface CheckBoxProperties extends ComponentProperties {
checked?: boolean;
}
export interface InputBoxProperties extends ComponentProperties {
value?: string;
inputType?: 'text' | 'number';
}
export interface DesignerTableProperties extends ComponentProperties {
/**
* the name of the properties to be displayed, properties not in this list will be accessible in details view.
*/
columns?: string[];
/**
* The display name of the object type
*/
objectTypeDisplayName: string;
/**
* the properties of the table data item
*/
itemProperties?: DesignerDataPropertyInfo[];
data?: DesignerTableComponentRowData[];
}
export interface DesignerTableComponentRowData {
[key: string]: InputBoxProperties | CheckBoxProperties | DropDownProperties | DesignerTableProperties;
}
export enum DesignerEditType {
Add = 0,
Remove = 1,
Update = 2
}
export interface DesignerEdit {
type: DesignerEditType;
property: DesignerEditIdentifier;
value: any;
}
export type DesignerEditIdentifier = string | { parentProperty: string, index: number, property: string };
export interface DesignerEditResult {
isValid: boolean;
errors?: { message: string, property?: DesignerEditIdentifier }[];
}
@@ -0,0 +1,77 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.designer-component, .designer-component .container {
width: 100%;
height: 100%;
}
.designer-component .content-container {
display: flex;
flex-direction: column;
border-right-width: 1px;
border-right-style: solid;
width: 100%;
height: 100%;
overflow: hidden;
}
.designer-component .top-content-container {
flex: 0 0 auto;
}
.designer-component .editor-container {
border-top-width: 1px;
border-top-style: solid;
width: 100%;
height: 100%;
}
.designer-component .tabbed-panel-container {
flex: 1 1 auto;
overflow: hidden;
}
.designer-component .properties-container {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.designer-component .properties-container .title-container {
padding: 5px;
flex: 0 0 auto;
font-weight: bold;
}
.designer-component .properties-container .properties-content {
flex: 1 1 auto;
overflow-y: auto;
}
.designer-component .component-label {
vertical-align: middle;
}
.designer-component .components-grid {
display: grid;
grid-template-columns: max-content auto; /* label, component*/
grid-template-rows: max-content;
grid-gap: 5px;
padding: 5px;
align-content: start;
box-sizing: border-box;
}
.designer-component .components-grid .full-row {
grid-area: span 1 / span 2; /* spans 1 row and 2 columns*/
}
.designer-component .monaco-table .slick-cell.editable {
padding: 0px;
border-width: 0px;
}
+6 -1
View File
@@ -61,7 +61,7 @@ export class InputBox extends vsInputBox {
if (_sqlOptions && _sqlOptions.type === 'textarea') {
this._isTextAreaInput = true;
}
this.required = !!this._sqlOptions.required;
this.required = !!this._sqlOptions?.required;
}
public override style(styles: IInputBoxStyles): void {
@@ -171,4 +171,9 @@ export class InputBox extends vsInputBox {
}
return undefined;
}
public override set width(width: number) {
super.width = width;
this.element.style.width = 'fit-content';
}
}
+6
View File
@@ -248,6 +248,12 @@ export class TabbedPanel extends Disposable {
}
}
public clearTabs(): void {
this._tabMap.forEach((value, key, map) => {
this.removeTab(key);
});
}
public removeTab(tab: PanelTabIdentifier) {
const actualTab = this._tabMap.get(tab);
if (!actualTab) {
@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/checkboxColumn.plugin';
import { BaseTableColumnOptions, TableColumn } from 'sql/base/browser/ui/table/plugins/tableColumn';
import { escape } from 'sql/base/common/strings';
import { Emitter } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
export interface CheckBoxCellValue {
enabled?: boolean;
checked: boolean;
}
export interface CheckBoxChangedEventArgs<T extends Slick.SlickData> {
item: T;
row: number;
column: number;
value: boolean;
}
export interface CheckBoxColumnOptions extends BaseTableColumnOptions {
}
export class CheckBoxColumn<T extends Slick.SlickData> implements Slick.Plugin<T>, TableColumn<T> {
private _handler = new Slick.EventHandler();
private _grid!: Slick.Grid<T>;
private _onChange = new Emitter<CheckBoxChangedEventArgs<T>>();
public onChange = this._onChange.event;
constructor(private options: CheckBoxColumnOptions) {
}
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(args));
this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyboardEvent(e as KeyboardEvent, args));
this._handler.subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs<T>) => { this.handleActiveCellChanged(args); });
}
public destroy(): void {
this._handler.unsubscribeAll();
}
public get definition(): Slick.Column<T> {
return {
id: this.options.field,
formatter: (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string => {
const cellValue = dataContext[columnDef.field] as CheckBoxCellValue;
const escapedTitle = escape(columnDef.name ?? '');
const disabledAttribute = cellValue.enabled === false ? 'disabled' : '';
const checkedAttribute = cellValue.checked ? 'checked' : '';
return `<input type="checkbox" tabindex=-1 title="${escapedTitle}" aria-label="${escapedTitle}" ${checkedAttribute} ${disabledAttribute}/>`;
},
field: this.options.field,
name: this.options.name,
resizable: this.options.resizable,
cssClass: 'slick-plugin-checkbox-column'
};
}
private getCheckbox(): HTMLInputElement {
const cellElement = this._grid.getActiveCellNode();
return cellElement.children[0] as HTMLInputElement;
}
private handleActiveCellChanged(args: Slick.OnActiveCellChangedEventArgs<T>): void {
if (this.isCurrentColumn(args.cell)) {
this.getCheckbox().focus();
}
}
private handleClick(args: Slick.OnClickEventArgs<T>): void {
if (this.isCurrentColumn(args.cell)) {
setTimeout(() => {
this.fireOnChangeEvent();
}, 0);
}
}
private handleKeyboardEvent(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Space) && this.isCurrentColumn(args.cell)) {
this.fireOnChangeEvent();
}
}
private fireOnChangeEvent(): void {
const cell = this._grid.getActiveCell();
const checked = this.getCheckbox().checked;
const item = this._grid.getDataItem(cell.row);
const cellValue = item[this.options.field] as CheckBoxCellValue;
if (checked !== cellValue.checked) {
this._onChange.fire({
row: cell.row,
column: cell.cell,
value: checked,
item: item
});
}
}
private isCurrentColumn(columnIndex: number): boolean {
return this._grid.getColumns()[columnIndex]?.id === this.definition.id;
}
}
@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.slick-plugin-checkbox-column {
text-align: center;
}
.slick-plugin-checkbox-column > input{
margin: 0px;
vertical-align: middle;
}
@@ -0,0 +1,172 @@
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { getCodeForKeyCode } from 'vs/base/browser/keyboardEvent';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { KeyCode } from 'vs/base/common/keyCodes';
import * as DOM from 'vs/base/browser/dom';
export interface ITableCellEditorOptions {
valueGetter?: (item: Slick.SlickData, column: Slick.Column<Slick.SlickData>) => string,
valueSetter?: (context: any, row: number, item: Slick.SlickData, column: Slick.Column<Slick.SlickData>, value: string) => Promise<void>,
optionsGetter?: (item: Slick.SlickData, column: Slick.Column<Slick.SlickData>) => string[],
editorStyler: (component: InputBox | SelectBox) => void
}
export class TableCellEditorFactory {
private _options: ITableCellEditorOptions;
constructor(options: ITableCellEditorOptions, private _contextViewProvider: IContextViewProvider) {
this._options = {
valueGetter: options.valueGetter ?? function (item, column) {
return item[column.field];
},
valueSetter: options.valueSetter ?? async function (context, row, item, column, value): Promise<void> {
item[column.field] = value;
},
optionsGetter: options.optionsGetter ?? function (item, column) {
return [];
},
editorStyler: options.editorStyler
};
}
public getTextEditorClass(context: any, inputType: 'text' | 'number' = 'text'): any {
const self = this;
class TextEditor {
private _originalValue: string;
private _input: InputBox;
private _keyCaptureList: number[];
constructor(private _args: Slick.Editors.EditorOptions<Slick.SlickData>) {
this.init();
const keycodesToCapture = [KeyCode.Home, KeyCode.End, KeyCode.UpArrow, KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow];
this._keyCaptureList = keycodesToCapture.map(keycode => getCodeForKeyCode(keycode));
}
/**
* The text editor should handle these key press events to avoid event bubble up
*/
public get keyCaptureList(): number[] {
return this._keyCaptureList;
}
public init(): void {
this._input = new InputBox(this._args.container, self._contextViewProvider, {
type: inputType
});
self._options.editorStyler(this._input);
this._input.element.style.height = '100%';
this._input.focus();
}
public destroy(): void {
this._input.dispose();
}
public focus(): void {
this._input.focus();
}
public loadValue(item: Slick.SlickData): void {
this._originalValue = self._options.valueGetter(item, this._args.column) ?? '';
this._input.value = this._originalValue;
}
public async applyValue(item: Slick.SlickData, state: string): Promise<void> {
const activeCell = this._args.grid.getActiveCell();
await self._options.valueSetter(context, activeCell.row, item, this._args.column, state);
}
public isValueChanged(): boolean {
return this._input.value !== this._originalValue.toString();
}
public serializeValue(): any {
return this._input.value;
}
public validate(): Slick.ValidateResults {
return {
valid: true,
msg: undefined
};
}
}
return TextEditor;
}
public getSelectBoxEditorClass(context: any, defaultOptions: string[]): any {
const self = this;
class TextEditor {
private _originalValue: string;
private _selectBox: SelectBox;
private _keyCaptureList: number[];
constructor(private _args: Slick.Editors.EditorOptions<Slick.SlickData>) {
this.init();
const keycodesToCapture = [KeyCode.Home, KeyCode.End, KeyCode.UpArrow, KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow];
this._keyCaptureList = keycodesToCapture.map(keycode => getCodeForKeyCode(keycode));
}
/**
* The text editor should handle these key press events to avoid event bubble up
*/
public get keyCaptureList(): number[] {
return this._keyCaptureList;
}
public init(): void {
const container = DOM.$('');
this._args.container.appendChild(container);
this._selectBox = new SelectBox([], undefined, self._contextViewProvider);
container.style.height = '100%';
container.style.width = '100%';
this._selectBox.render(container);
this._selectBox.selectElem.style.height = '100%';
self._options.editorStyler(this._selectBox);
this._selectBox.focus();
}
public destroy(): void {
this._selectBox.dispose();
}
public focus(): void {
this._selectBox.focus();
}
public loadValue(item: Slick.SlickData): void {
this._originalValue = self._options.valueGetter(item, this._args.column) ?? '';
const options = self._options.optionsGetter(item, this._args.column) ?? defaultOptions;
const idx = options?.indexOf(this._originalValue);
if (idx > -1) {
this._selectBox.setOptions(options);
this._selectBox.select(idx);
}
}
public async applyValue(item: Slick.SlickData, state: string): Promise<void> {
const activeCell = this._args.grid.getActiveCell();
await self._options.valueSetter(context, activeCell.row, item, this._args.column, state);
}
public isValueChanged(): boolean {
return this._selectBox.value !== this._originalValue.toString();
}
public serializeValue(): any {
return this._selectBox.value;
}
public validate(): Slick.ValidateResults {
return {
valid: true,
msg: undefined
};
}
}
return TextEditor;
}
}
@@ -9,11 +9,11 @@ import { RawContextKey, IContextKey, ContextKeyExpr, IContextKeyService } from '
import { DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { createStyleSheet } from 'vs/base/browser/dom';
import { attachHighPerfTableStyler as attachTableStyler, defaultHighPerfTableStyles } from 'sql/platform/theme/common/styler';
import { attachHighPerfTableStyler as attachTableStyler, defaultHighPerfTableStyles, IHighPerfTableStyleOverrides } from 'sql/platform/theme/common/styler';
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ITableDataSource, ITableColumn } from 'sql/base/browser/ui/table/highPerf/table';
import { IColorMapping, computeStyles } from 'vs/platform/theme/common/styler';
import { computeStyles } from 'vs/platform/theme/common/styler';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export const ITableService = createDecorator<ITableService>('tableService');
@@ -118,7 +118,7 @@ function toWorkbenchTableOptions<T>(options: ITableOptions<T>): [ITableOptions<T
}
export interface IWorkbenchTableOptions<T> extends ITableOptions<T> {
readonly overrideStyles?: IColorMapping;
readonly overrideStyles?: IHighPerfTableStyleOverrides;
}
export class WorkbenchTable<T> extends Table<T> {
+199 -158
View File
@@ -8,103 +8,113 @@ import * as colors from './colors';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import * as cr from 'vs/platform/theme/common/colorRegistry';
import * as sqlcr from 'sql/platform/theme/common/colorRegistry';
import { attachStyler, defaultListStyles, IColorMapping, IStyleOverrides } from 'vs/platform/theme/common/styler';
import { attachStyler, computeStyles, defaultListStyles, IColorMapping, IStyleOverrides } from 'vs/platform/theme/common/styler';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IThemable } from 'vs/base/common/styler';
export function attachDropdownStyler(widget: IThemable, themeService: IThemeService, style?:
{
backgroundColor?: cr.ColorIdentifier,
foregroundColor?: cr.ColorIdentifier,
borderColor?: cr.ColorIdentifier,
buttonForeground?: cr.ColorIdentifier,
buttonBackground?: cr.ColorIdentifier,
buttonHoverBackground?: cr.ColorIdentifier,
buttonFocusOutline?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
foregroundColor: (style && style.foregroundColor) || cr.inputForeground,
borderColor: (style && style.borderColor) || cr.inputBorder,
backgroundColor: (style && style.backgroundColor) || cr.editorBackground,
buttonForeground: (style && style.buttonForeground) || cr.buttonForeground,
buttonBackground: (style && style.buttonBackground) || cr.buttonBackground,
buttonHoverBackground: (style && style.buttonHoverBackground) || cr.buttonHoverBackground,
buttonBorder: cr.contrastBorder,
buttonFocusOutline: (style && style.buttonFocusOutline) || colors.buttonFocusOutline
}, widget);
export interface IDropdownStyleOverrides extends IStyleOverrides {
foregroundColor?: cr.ColorIdentifier;
borderColor?: cr.ColorIdentifier;
backgroundColor?: cr.ColorIdentifier;
buttonForeground?: cr.ColorIdentifier;
buttonBackground?: cr.ColorIdentifier;
buttonHoverBackground?: cr.ColorIdentifier;
buttonBorder?: cr.ColorIdentifier;
buttonFocusOutline?: cr.ColorIdentifier;
}
export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
inputBackground?: cr.ColorIdentifier,
inputForeground?: cr.ColorIdentifier,
disabledInputBackground?: cr.ColorIdentifier,
disabledInputForeground?: cr.ColorIdentifier,
inputBorder?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
inputBackground: (style && style.inputBackground) || cr.inputBackground,
inputForeground: (style && style.inputForeground) || cr.inputForeground,
disabledInputBackground: (style && style.disabledInputBackground) || colors.disabledInputBackground,
disabledInputForeground: (style && style.disabledInputForeground) || colors.disabledInputForeground,
inputBorder: (style && style.inputBorder) || cr.inputBorder,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground
}, widget);
export const defaultDropdownStyle: IDropdownStyleOverrides = {
foregroundColor: cr.inputForeground,
borderColor: cr.inputBorder,
backgroundColor: cr.editorBackground,
buttonForeground: cr.buttonForeground,
buttonBackground: cr.buttonBackground,
buttonHoverBackground: cr.buttonHoverBackground,
buttonBorder: cr.contrastBorder,
buttonFocusOutline: colors.buttonFocusOutline
};
export function attachDropdownStyler(widget: IThemable, themeService: IThemeService, style?: IDropdownStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultDropdownStyle, ...(style || {}) }, widget);
}
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?:
{
selectBackground?: cr.ColorIdentifier,
selectListBackground?: cr.ColorIdentifier,
selectForeground?: cr.ColorIdentifier,
selectBorder?: cr.ColorIdentifier,
disabledSelectBackground?: cr.ColorIdentifier,
disabledSelectForeground?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier,
focusBorder?: cr.ColorIdentifier,
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listFocusOutline?: cr.ColorIdentifier,
listHoverBackground?: cr.ColorIdentifier,
listHoverForeground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
selectBackground: (style && style.selectBackground) || cr.selectBackground,
selectListBackground: (style && style.selectListBackground) || cr.selectListBackground,
selectForeground: (style && style.selectForeground) || cr.selectForeground,
selectBorder: (style && style.selectBorder) || cr.selectBorder,
disabledSelectBackground: (style && style.disabledSelectBackground) || colors.disabledInputBackground,
disabledSelectForeground: (style && style.disabledSelectForeground) || colors.disabledInputForeground,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground,
focusBorder: (style && style.focusBorder) || cr.focusBorder,
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listHoverOutline: (style && style.listFocusOutline) || cr.activeContrastBorder
}, widget);
export interface IInputBoxStyleOverrides extends IStyleOverrides {
inputBackground?: cr.ColorIdentifier,
inputForeground?: cr.ColorIdentifier,
disabledInputBackground?: cr.ColorIdentifier,
disabledInputForeground?: cr.ColorIdentifier,
inputBorder?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier
}
export const defaultInputBoxStyleOverrides: IInputBoxStyleOverrides = {
inputBackground: cr.inputBackground,
inputForeground: cr.inputForeground,
disabledInputBackground: colors.disabledInputBackground,
disabledInputForeground: colors.disabledInputForeground,
inputBorder: cr.inputBorder,
inputValidationInfoBorder: cr.inputValidationInfoBorder,
inputValidationInfoBackground: cr.inputValidationInfoBackground,
inputValidationWarningBorder: cr.inputValidationWarningBorder,
inputValidationWarningBackground: cr.inputValidationWarningBackground,
inputValidationErrorBorder: cr.inputValidationErrorBorder,
inputValidationErrorBackground: cr.inputValidationErrorBackground
};
export function attachInputBoxStyler(widget: IThemable, themeService: IThemeService, style?: IInputBoxStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultInputBoxStyleOverrides, ...(style || {}) }, widget);
}
export interface ISelectBoxStyleOverrides extends IStyleOverrides {
selectBackground?: cr.ColorIdentifier,
selectListBackground?: cr.ColorIdentifier,
selectForeground?: cr.ColorIdentifier,
selectBorder?: cr.ColorIdentifier,
disabledSelectBackground?: cr.ColorIdentifier,
disabledSelectForeground?: cr.ColorIdentifier,
inputValidationInfoBorder?: cr.ColorIdentifier,
inputValidationInfoBackground?: cr.ColorIdentifier,
inputValidationWarningBorder?: cr.ColorIdentifier,
inputValidationWarningBackground?: cr.ColorIdentifier,
inputValidationErrorBorder?: cr.ColorIdentifier,
inputValidationErrorBackground?: cr.ColorIdentifier,
focusBorder?: cr.ColorIdentifier,
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listFocusOutline?: cr.ColorIdentifier,
listHoverBackground?: cr.ColorIdentifier,
listHoverForeground?: cr.ColorIdentifier
}
export const defaultSelectBoxStyleOverrides: ISelectBoxStyleOverrides = {
selectBackground: cr.selectBackground,
selectListBackground: cr.selectListBackground,
selectForeground: cr.selectForeground,
selectBorder: cr.selectBorder,
disabledSelectBackground: colors.disabledInputBackground,
disabledSelectForeground: colors.disabledInputForeground,
inputValidationInfoBorder: cr.inputValidationInfoBorder,
inputValidationInfoBackground: cr.inputValidationInfoBackground,
inputValidationWarningBorder: cr.inputValidationWarningBorder,
inputValidationWarningBackground: cr.inputValidationWarningBackground,
inputValidationErrorBorder: cr.inputValidationErrorBorder,
inputValidationErrorBackground: cr.inputValidationErrorBackground,
focusBorder: cr.focusBorder,
listFocusBackground: cr.listFocusBackground,
listFocusForeground: cr.listFocusForeground,
listFocusOutline: cr.activeContrastBorder,
listHoverBackground: cr.listHoverBackground,
listHoverForeground: cr.listHoverForeground,
listHoverOutline: cr.activeContrastBorder
};
export function attachSelectBoxStyler(widget: IThemable, themeService: IThemeService, style?: ISelectBoxStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultSelectBoxStyleOverrides, ...(style || {}) }, widget);
}
export function attachListBoxStyler(widget: IThemable, themeService: IThemeService, style?:
@@ -132,7 +142,7 @@ export function attachListBoxStyler(widget: IThemable, themeService: IThemeServi
}, widget);
}
export function attachTableStyler(widget: IThemable, themeService: IThemeService, style?: {
export interface ITableStyleOverrides extends IStyleOverrides {
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listActiveSelectionBackground?: cr.ColorIdentifier,
@@ -150,31 +160,35 @@ export function attachTableStyler(widget: IThemable, themeService: IThemeService
listSelectionOutline?: cr.ColorIdentifier,
listHoverOutline?: cr.ColorIdentifier,
tableHeaderBackground?: cr.ColorIdentifier,
tableHeaderForeground?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.listActiveSelectionBackground,
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || colors.listFocusAndSelectionBackground,
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground,
listInactiveFocusBackground: (style && style.listInactiveFocusBackground),
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listDropBackground: (style && style.listDropBackground) || cr.listDropBackground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder,
listInactiveFocusOutline: style && style.listInactiveFocusOutline,
tableHeaderBackground: (style && style.tableHeaderBackground) || colors.tableHeaderBackground,
tableHeaderForeground: (style && style.tableHeaderForeground) || colors.tableHeaderForeground
}, widget);
tableHeaderForeground?: cr.ColorIdentifier,
}
export interface ITableStyleOverrides extends IStyleOverrides {
export const defaultTableStyleOverrides: ITableStyleOverrides = {
listFocusBackground: cr.listFocusBackground,
listFocusForeground: cr.listFocusForeground,
listActiveSelectionBackground: cr.listActiveSelectionBackground,
listActiveSelectionForeground: cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: colors.listFocusAndSelectionBackground,
listFocusAndSelectionForeground: cr.listActiveSelectionForeground,
listInactiveFocusBackground: cr.listInactiveFocusBackground,
listInactiveSelectionBackground: cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: cr.listInactiveSelectionForeground,
listHoverBackground: cr.listHoverBackground,
listHoverForeground: cr.listHoverForeground,
listDropBackground: cr.listDropBackground,
listFocusOutline: cr.activeContrastBorder,
listSelectionOutline: cr.activeContrastBorder,
listHoverOutline: cr.activeContrastBorder,
listInactiveFocusOutline: cr.listInactiveFocusOutline,
tableHeaderBackground: colors.tableHeaderBackground,
tableHeaderForeground: colors.tableHeaderForeground
};
export function attachTableStyler(widget: IThemable, themeService: IThemeService, style?: ITableStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultTableStyleOverrides, ...(style || {}) }, widget);
}
export interface IHighPerfTableStyleOverrides extends IStyleOverrides {
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listActiveSelectionBackground?: cr.ColorIdentifier,
@@ -197,10 +211,6 @@ export interface ITableStyleOverrides extends IStyleOverrides {
tableHeaderAndRowCountColor?: cr.ColorIdentifier
}
export function attachHighPerfTableStyler(widget: IThemable, themeService: IThemeService, overrides?: IColorMapping): IDisposable {
return attachStyler(themeService, { ...defaultHighPerfTableStyles, ...(overrides || {}) }, widget);
}
export const defaultHighPerfTableStyles: IColorMapping = {
listFocusBackground: cr.listFocusBackground,
listFocusForeground: cr.listFocusForeground,
@@ -223,7 +233,11 @@ export const defaultHighPerfTableStyles: IColorMapping = {
tableHeaderAndRowCountColor: colors.tableCellOutline
};
export function attachEditableDropdownStyler(widget: IThemable, themeService: IThemeService, style?: {
export function attachHighPerfTableStyler(widget: IThemable, themeService: IThemeService, overrides?: IHighPerfTableStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultHighPerfTableStyles, ...(overrides || {}) }, widget);
}
export interface IEditableDropdownStyleOverrides extends IStyleOverrides {
listFocusBackground?: cr.ColorIdentifier,
listFocusForeground?: cr.ColorIdentifier,
listActiveSelectionBackground?: cr.ColorIdentifier,
@@ -240,7 +254,6 @@ export function attachEditableDropdownStyler(widget: IThemable, themeService: IT
listInactiveFocusOutline?: cr.ColorIdentifier,
listSelectionOutline?: cr.ColorIdentifier,
listHoverOutline?: cr.ColorIdentifier,
inputBackground?: cr.ColorIdentifier,
inputForeground?: cr.ColorIdentifier,
inputBorder?: cr.ColorIdentifier,
@@ -252,43 +265,53 @@ export function attachEditableDropdownStyler(widget: IThemable, themeService: IT
inputValidationErrorBackground?: cr.ColorIdentifier,
contextBackground?: cr.ColorIdentifier,
contextBorder?: cr.ColorIdentifier
}): IDisposable {
return attachStyler(themeService, {
listFocusBackground: (style && style.listFocusBackground) || cr.listFocusBackground,
listFocusForeground: (style && style.listFocusForeground) || cr.listFocusForeground,
listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || cr.lighten(cr.listActiveSelectionBackground, 0.1),
listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || cr.listActiveSelectionBackground,
listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || cr.listActiveSelectionForeground,
listInactiveFocusBackground: (style && style.listInactiveFocusBackground),
listInactiveSelectionBackground: (style && style.listInactiveSelectionBackground) || cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: (style && style.listInactiveSelectionForeground) || cr.listInactiveSelectionForeground,
listHoverBackground: (style && style.listHoverBackground) || cr.listHoverBackground,
listHoverForeground: (style && style.listHoverForeground) || cr.listHoverForeground,
listDropBackground: (style && style.listDropBackground) || cr.listDropBackground,
listFocusOutline: (style && style.listFocusOutline) || cr.activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || cr.activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || cr.activeContrastBorder,
listInactiveFocusOutline: style && style.listInactiveFocusOutline,
inputBackground: (style && style.inputBackground) || cr.inputBackground,
inputForeground: (style && style.inputForeground) || cr.inputForeground,
inputBorder: (style && style.inputBorder) || cr.inputBorder,
inputValidationInfoBorder: (style && style.inputValidationInfoBorder) || cr.inputValidationInfoBorder,
inputValidationInfoBackground: (style && style.inputValidationInfoBackground) || cr.inputValidationInfoBackground,
inputValidationWarningBorder: (style && style.inputValidationWarningBorder) || cr.inputValidationWarningBorder,
inputValidationWarningBackground: (style && style.inputValidationWarningBackground) || cr.inputValidationWarningBackground,
inputValidationErrorBorder: (style && style.inputValidationErrorBorder) || cr.inputValidationErrorBorder,
inputValidationErrorBackground: (style && style.inputValidationErrorBackground) || cr.inputValidationErrorBackground,
contextBackground: (style && style.contextBackground) || cr.editorBackground,
contextBorder: (style && style.contextBorder) || cr.inputBorder
}, widget);
}
export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: { disabledCheckboxForeground?: cr.ColorIdentifier })
: IDisposable {
return attachStyler(themeService, {
disabledCheckboxForeground: (style && style.disabledCheckboxForeground) || colors.disabledCheckboxForeground
}, widget);
export const defaultEditableDropdownStyleOverrides: IEditableDropdownStyleOverrides = {
listFocusBackground: cr.listFocusBackground,
listFocusForeground: cr.listFocusForeground,
listActiveSelectionBackground: cr.listActiveSelectionBackground,
listActiveSelectionForeground: cr.listActiveSelectionForeground,
listFocusAndSelectionBackground: cr.listActiveSelectionBackground,
listFocusAndSelectionForeground: cr.listActiveSelectionForeground,
listInactiveFocusBackground: cr.listInactiveFocusBackground,
listInactiveSelectionBackground: cr.listInactiveSelectionBackground,
listInactiveSelectionForeground: cr.listInactiveSelectionForeground,
listHoverBackground: cr.listHoverBackground,
listHoverForeground: cr.listHoverForeground,
listDropBackground: cr.listDropBackground,
listFocusOutline: cr.activeContrastBorder,
listSelectionOutline: cr.activeContrastBorder,
listHoverOutline: cr.activeContrastBorder,
listInactiveFocusOutline: cr.listInactiveFocusOutline,
inputBackground: cr.inputBackground,
inputForeground: cr.inputForeground,
inputBorder: cr.inputBorder,
inputValidationInfoBorder: cr.inputValidationInfoBorder,
inputValidationInfoBackground: cr.inputValidationInfoBackground,
inputValidationWarningBorder: cr.inputValidationWarningBorder,
inputValidationWarningBackground: cr.inputValidationWarningBackground,
inputValidationErrorBorder: cr.inputValidationErrorBorder,
inputValidationErrorBackground: cr.inputValidationErrorBackground,
contextBackground: cr.editorBackground,
contextBorder: cr.inputBorder
};
export function attachEditableDropdownStyler(widget: IThemable, themeService: IThemeService, style?: IEditableDropdownStyleOverrides): IDisposable {
return attachStyler(themeService, { ...defaultEditableDropdownStyleOverrides, ...(style || {}) }, widget);
}
export interface ICheckboxStyleOverrides extends IStyleOverrides {
disabledCheckboxForeground?: cr.ColorIdentifier
}
export const defaultCheckboxStyleOverrides: ICheckboxStyleOverrides = {
disabledCheckboxForeground: colors.disabledCheckboxForeground
};
export function attachCheckboxStyler(widget: IThemable, themeService: IThemeService, style?: ICheckboxStyleOverrides): IDisposable {
return attachStyler(themeService, {}, widget);
}
export interface IInfoBoxStyleOverrides {
@@ -329,9 +352,7 @@ export function attachInfoButtonStyler(widget: IThemable, themeService: IThemeSe
export function attachTableFilterStyler(widget: IThemable, themeService: IThemeService): IDisposable {
return attachStyler(themeService, {
inputBackground: cr.inputBackground,
inputForeground: cr.inputForeground,
inputBorder: cr.inputBorder,
...defaultInputBoxStyleOverrides,
buttonForeground: cr.buttonForeground,
buttonBackground: cr.buttonBackground,
buttonHoverBackground: cr.buttonHoverBackground,
@@ -349,3 +370,23 @@ export function attachTableFilterStyler(widget: IThemable, themeService: IThemeS
...defaultListStyles,
}, widget);
}
export function attachDesignerStyler(widget: any, themeService: IThemeService): IDisposable {
function applyStyles(): void {
const colorTheme = themeService.getColorTheme();
const inputStyles = computeStyles(colorTheme, defaultInputBoxStyleOverrides);
const selectBoxStyles = computeStyles(colorTheme, defaultSelectBoxStyleOverrides);
const tableStyles = computeStyles(colorTheme, defaultTableStyleOverrides);
const checkboxStyles = computeStyles(colorTheme, defaultCheckboxStyleOverrides);
widget.style({
inputBoxStyles: inputStyles,
selectBoxStyles: selectBoxStyles,
tableStyles: tableStyles,
checkboxStyles: checkboxStyles
});
}
applyStyles();
return themeService.onDidColorThemeChange(applyStyles);
}
@@ -30,6 +30,7 @@ import { IAssessmentService } from 'sql/workbench/services/assessment/common/int
import { IDataGridProviderService } from 'sql/workbench/services/dataGridProvider/common/dataGridProviderService';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { ITableDesignerService } from 'sql/workbench/services/tableDesigner/common/interface';
/**
* Main thread class for handling data protocol management registration.
@@ -59,7 +60,8 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
@IFileBrowserService private _fileBrowserService: IFileBrowserService,
@IAssessmentService private _assessmentService: IAssessmentService,
@IDataGridProviderService private _dataGridProviderService: IDataGridProviderService,
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService,
@ITableDesignerService private _tableDesignerService: ITableDesignerService
) {
super();
if (extHostContext) {
@@ -507,6 +509,20 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
return undefined;
}
$registerTableDesignerProvider(providerId: string, handle: number): Promise<any> {
const self = this;
this._tableDesignerService.registerProvider(providerId, <azdata.designers.TableDesignerProvider>{
getTableDesignerInfo(tableInfo: azdata.designers.TableInfo): Thenable<azdata.designers.TableDesignerInfo> {
return self._proxy.$getTableDesignerInfo(handle, tableInfo);
},
processTableEdit(table, data, edit): Thenable<azdata.designers.DesignerEditResult> {
return self._proxy.$processTableDesignerEdit(handle, table, data, edit);
}
});
return undefined;
}
public $registerSerializationProvider(providerId: string, handle: number): Promise<any> {
const self = this;
this._serializationService.registerProvider(providerId, <azdata.SerializationProvider>{
@@ -616,6 +632,11 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
this._jobManagementService.fireOnDidChange();
}
// Table Designer
public $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo): void {
this._tableDesignerService.openTableDesigner(providerId, tableInfo);
}
public $unregisterProvider(handle: number): Promise<any> {
let capabilitiesRegistration = this._capabilitiesRegistrations[handle];
if (capabilitiesRegistration) {
@@ -194,6 +194,12 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
return rt;
}
$registerTableDesignerProvider(provider: azdata.designers.TableDesignerProvider): vscode.Disposable {
let rt = this.registerProvider(provider, DataProviderType.TableDesignerProvider);
this._proxy.$registerTableDesignerProvider(provider.providerId, provider.handle);
return rt;
}
// Capabilities Discovery handlers
override $getServerCapabilities(handle: number, client: azdata.DataProtocolClientCapabilities): Thenable<azdata.DataProtocolServerCapabilities> {
return this._resolveProvider<azdata.CapabilitiesProvider>(handle).getServerCapabilities(client);
@@ -884,4 +890,18 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
public override $getDataGridColumns(handle: number): Thenable<azdata.DataGridColumn[]> {
return this._resolveProvider<azdata.DataGridProvider>(handle).getDataGridColumns();
}
// Table Designer
public override $getTableDesignerInfo(handle, table: azdata.designers.TableInfo): Thenable<azdata.designers.TableDesignerInfo> {
return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).getTableDesignerInfo(table);
}
public override $processTableDesignerEdit(handle, table: azdata.designers.TableInfo, data: azdata.designers.DesignerData, edit: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult> {
return this._resolveProvider<azdata.designers.TableDesignerProvider>(handle).processTableEdit(table, data, edit);
}
public override $openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo): Promise<void> {
this._proxy.$openTableDesigner(providerId, tableInfo);
return Promise.resolve();
}
}
@@ -380,6 +380,10 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
return extHostDataProvider.$registerDataGridProvider(provider);
};
let registerTableDesignerProvider = (provider: azdata.designers.TableDesignerProvider): vscode.Disposable => {
return extHostDataProvider.$registerTableDesignerProvider(provider);
};
// namespace: dataprotocol
const dataprotocol: typeof azdata.dataprotocol = {
registerBackupProvider,
@@ -400,6 +404,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
registerSerializationProvider,
registerSqlAssessmentServicesProvider,
registerDataGridProvider,
registerTableDesignerProvider,
onDidChangeLanguageFlavor(listener: (e: azdata.DidChangeLanguageFlavorParams) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
return extHostDataProvider.onDidChangeLanguageFlavor(listener, thisArgs, disposables);
},
@@ -564,6 +569,15 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
SqlAssessmentTargetType: sqlExtHostTypes.SqlAssessmentTargetType
};
const designers: typeof azdata.designers = {
TableProperty: sqlExtHostTypes.designers.TableProperty,
TableColumnProperty: sqlExtHostTypes.designers.TableColumnProperty,
DesignerEditType: sqlExtHostTypes.designers.DesignerEditType,
openTableDesigner(providerId, tableInfo: azdata.designers.TableInfo): Promise<void> {
return extHostDataProvider.$openTableDesigner(providerId, tableInfo);
}
};
return {
version: initData.version,
accounts,
@@ -614,7 +628,8 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
DatabaseEngineEdition: sqlExtHostTypes.DatabaseEngineEdition,
TabOrientation: sqlExtHostTypes.TabOrientation,
sqlAssessment,
TextType: sqlExtHostTypes.TextType
TextType: sqlExtHostTypes.TextType,
designers: designers
};
}
};
@@ -526,6 +526,21 @@ export abstract class ExtHostDataProtocolShape {
* Gets the list of columns for a data grid
*/
$getDataGridColumns(handle: number): Thenable<azdata.DataGridColumn[]> { throw ni(); }
/**
* Gets the table designer info for the specified table
*/
$getTableDesignerInfo(handle, table: azdata.designers.TableInfo): Thenable<azdata.designers.TableDesignerInfo> { throw ni(); }
/**
* Process the table edit.
*/
$processTableDesignerEdit(handle, table: azdata.designers.TableInfo, data: azdata.designers.DesignerData, edit: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult> { throw ni(); }
/**
* Open a new instance of table designer.
*/
$openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo, designerInfo: azdata.designers.TableDesignerInfo): void { throw ni(); }
}
/**
@@ -591,6 +606,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
$registerSerializationProvider(providerId: string, handle: number): Promise<any>;
$registerSqlAssessmentServicesProvider(providerId: string, handle: number): Promise<any>;
$registerDataGridProvider(providerId: string, title: string, handle: number): void;
$registerTableDesignerProvider(providerId: string, handle: number): Promise<any>;
$unregisterProvider(handle: number): Promise<any>;
$onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void;
$onIntelliSenseCacheComplete(handle: number, connectionUri: string): void;
@@ -614,6 +630,7 @@ export interface MainThreadDataProtocolShape extends IDisposable {
$onSessionStopped(handle: number, response: azdata.ProfilerSessionStoppedParams): void;
$onProfilerSessionCreated(handle: number, response: azdata.ProfilerSessionCreatedParams): void;
$onJobDataUpdated(handle: Number): void;
$openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo): void;
/**
* Callback when a session has completed initialization
@@ -381,7 +381,8 @@ export enum DataProviderType {
SerializationProvider = 'SerializationProvider',
IconProvider = 'IconProvider',
SqlAssessmentServicesProvider = 'SqlAssessmentServicesProvider',
DataGridProvider = 'DataGridProvider'
DataGridProvider = 'DataGridProvider',
TableDesignerProvider = 'TableDesignerProvider'
}
export enum DeclarativeDataType {
@@ -905,3 +906,27 @@ export enum TextType {
UnorderedList = 'UnorderedList',
OrderedList = 'OrderedList'
}
export namespace designers {
export enum TableProperty {
Schema = 'schema',
Name = 'name',
Description = 'description',
Columns = 'columns',
Script = 'script'
}
export enum TableColumnProperty {
Name = 'name',
Type = 'type',
AllowNulls = 'allowNulls',
DefaultValue = 'defaultValue',
Length = 'length'
}
export enum DesignerEditType {
Add = 0,
Remove = 1,
Update = 2
}
}
@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { URI } from 'vs/workbench/workbench.web.api';
import { TableDesignerComponentInput } from 'sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput';
import { TableDesignerProvider } from 'sql/workbench/services/tableDesigner/common/interface';
import * as azdata from 'azdata';
const NewTable: string = localize('tableDesigner.newTable', "New Table");
export class TableDesignerInput extends EditorInput {
public static ID: string = 'workbench.editorinputs.tableDesignerInput';
private _designerComponentInput: TableDesignerComponentInput;
constructor(provider: TableDesignerProvider,
private _tableInfo: azdata.designers.TableInfo) {
super();
this._designerComponentInput = new TableDesignerComponentInput(provider, this._tableInfo);
}
get typeId(): string {
return TableDesignerInput.ID;
}
get resource(): URI {
return undefined;
}
public getComponentInput(): TableDesignerComponentInput {
return this._designerComponentInput;
}
override getName(): string {
const tableName = this._tableInfo.isNewTable ? NewTable : `${this._tableInfo.schema}.${this._tableInfo.name}`;
return `${this._tableInfo.server}.${this._tableInfo.database} - ${tableName}`;
}
}
@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TableDesignerInput } from 'sql/workbench/browser/editor/tableDesigner/tableDesignerInput';
import { TableDesignerEditor } from 'sql/workbench/contrib/tableDesigner/browser/tableDesignerEditor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, IEditorRegistry } from 'vs/workbench/browser/editor';
import { EditorExtensions } from 'vs/workbench/common/editor';
const tableDesignerDescriptor = EditorDescriptor.create(
TableDesignerEditor,
TableDesignerEditor.ID,
'TableDesignerEditor'
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
.registerEditor(tableDesignerDescriptor, [new SyncDescriptor(TableDesignerInput)]);
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Designer } from 'sql/base/browser/ui/designer/designer';
import { attachDesignerStyler } from 'sql/platform/theme/common/styler';
import { TableDesignerInput } from 'sql/workbench/browser/editor/tableDesigner/tableDesignerInput';
import * as DOM from 'vs/base/browser/dom';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IEditorOptions } from 'vs/platform/editor/common/editor';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IEditorOpenContext } from 'vs/workbench/common/editor';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
export class TableDesignerEditor extends EditorPane {
public static readonly ID: string = 'workbench.editor.tableDesigner';
private _designer: Designer;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
@IStorageService storageService: IStorageService,
@IContextViewService private _contextViewService: IContextViewService
) {
super(TableDesignerEditor.ID, telemetryService, themeService, storageService);
}
public override get input(): TableDesignerInput | undefined {
return this._input as TableDesignerInput;
}
override async setInput(input: TableDesignerInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
await super.setInput(input, options, context, token);
this._designer.setInput(input.getComponentInput());
}
protected createEditor(parent: HTMLElement): void {
// The editor is only created once per editor group.
this._designer = new Designer(parent, this._contextViewService);
this._register(attachDesignerStyler(this._designer, this.themeService));
}
layout(dimension: DOM.Dimension): void {
this._designer.layout(dimension);
}
}
@@ -13,12 +13,14 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
static Status = new RawContextKey<string>('nodeStatus', undefined);
static TreeNode = new RawContextKey<TreeNode>('treeNode', undefined);
static NodeLabel = new RawContextKey<string>('nodeLabel', undefined);
static NodePath = new RawContextKey<string>('nodePath', undefined);
private _nodeTypeKey: IContextKey<string>;
private _subTypeKey: IContextKey<string>;
private _statusKey: IContextKey<string>;
private _treeNodeKey: IContextKey<TreeNode>;
private _nodeLabelKey: IContextKey<string>;
private _nodePathKey: IContextKey<string>;
constructor(
@IContextKeyService contextKeyService: IContextKeyService
@@ -28,6 +30,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._statusKey = TreeNodeContextKey.Status.bindTo(contextKeyService);
this._treeNodeKey = TreeNodeContextKey.TreeNode.bindTo(contextKeyService);
this._nodeLabelKey = TreeNodeContextKey.NodeLabel.bindTo(contextKeyService);
this._nodePathKey = TreeNodeContextKey.NodePath.bindTo(contextKeyService);
}
set(value: TreeNode) {
@@ -38,6 +41,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._statusKey.set(value && value.nodeStatus);
}
this._nodeLabelKey.set(value && value.label);
this._nodePathKey.set(value && value.nodePath);
}
reset(): void {
@@ -46,6 +50,7 @@ export class TreeNodeContextKey implements IContextKey<TreeNode> {
this._statusKey.reset();
this._treeNodeKey.reset();
this._nodeLabelKey.reset();
this._nodePathKey.reset();
}
public get(): TreeNode | undefined {
@@ -0,0 +1,177 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { DesignerData, DesignerEdit, DesignerEditResult, DesignerComponentInput, DesignerView, DesignerTab, DesignerDataPropertyInfo, DropDownProperties, DesignerTableProperties } from 'sql/base/browser/ui/designer/interfaces';
import { TableDesignerProvider } from 'sql/workbench/services/tableDesigner/common/interface';
import { localize } from 'vs/nls';
import { designers } from 'sql/workbench/api/common/sqlExtHostTypes';
export class TableDesignerComponentInput implements DesignerComponentInput {
private _data: DesignerData;
private _view: DesignerView;
constructor(private readonly _provider: TableDesignerProvider,
private _tableInfo: azdata.designers.TableInfo) {
}
get objectTypeDisplayName(): string {
return localize('tableDesigner.tableObjectType', "Table");
}
async getView(): Promise<DesignerView> {
if (!this._view) {
await this.initialize();
}
return this._view;
}
async getData(): Promise<DesignerData> {
if (!this._data) {
await this.initialize();
}
return this._data;
}
async processEdit(edit: DesignerEdit): Promise<DesignerEditResult> {
const result = await this._provider.processTableEdit(this._tableInfo, this._data!, edit);
if (result.isValid) {
this._data = result.data;
}
return {
isValid: result.isValid,
errors: result.errors
};
}
private async initialize(): Promise<void> {
const designerInfo = await this._provider.getTableDesignerInfo(this._tableInfo);
this._data = designerInfo.data;
this.setDefaultData();
const advancedTabComponents: DesignerDataPropertyInfo[] = [
{
componentType: 'dropdown',
propertyName: designers.TableProperty.Schema,
componentProperties: <DropDownProperties>{
title: localize('tableDesigner.schemaTitle', "Schema"),
values: designerInfo.schemas
}
}, {
componentType: 'input',
propertyName: designers.TableProperty.Description,
componentProperties: {
title: localize('tableDesigner.descriptionTitle', "Description")
}
}
];
if (designerInfo.view.additionalTableProperties) {
advancedTabComponents.push(...designerInfo.view.additionalTableProperties);
}
const advancedTab = <DesignerTab>{
title: localize('tableDesigner.advancedTab', "Advanced"),
components: advancedTabComponents
};
const columnProperties: DesignerDataPropertyInfo[] = [
{
componentType: 'input',
propertyName: designers.TableColumnProperty.Name,
componentProperties: {
title: localize('tableDesigner.columnNameTitle', "Name"),
width: 150
}
}, {
componentType: 'dropdown',
propertyName: designers.TableColumnProperty.Type,
componentProperties: {
title: localize('tableDesigner.columnTypeTitle', "Type"),
width: 100,
values: designerInfo.columnTypes
}
}, {
componentType: 'input',
propertyName: designers.TableColumnProperty.Length,
componentProperties: {
title: localize('tableDesigner.columnLengthTitle', "Length"),
width: 75
}
}, {
componentType: 'input',
propertyName: designers.TableColumnProperty.DefaultValue,
componentProperties: {
title: localize('tableDesigner.columnDefaultValueTitle', "Default Value"),
width: 150
}
}, {
componentType: 'checkbox',
propertyName: designers.TableColumnProperty.AllowNulls,
componentProperties: {
title: localize('tableDesigner.columnAllowNullTitle', "Allow Nulls"),
}
}
];
if (designerInfo.view.addtionalTableColumnProperties) {
columnProperties.push(...designerInfo.view.addtionalTableColumnProperties);
}
const columnsTab = <DesignerTab>{
title: localize('tableDesigner.columnsTabTitle', "Columns"),
components: [
{
componentType: 'table',
propertyName: designers.TableProperty.Columns,
componentProperties: <DesignerTableProperties>{
ariaLabel: localize('tableDesigner.columnsTabTitle', "Columns"),
columns: [
designers.TableColumnProperty.Name,
designers.TableColumnProperty.Type,
designers.TableColumnProperty.Length,
designers.TableColumnProperty.DefaultValue,
designers.TableColumnProperty.AllowNulls
],
itemProperties: columnProperties,
objectTypeDisplayName: localize('tableDesigner.columnTypeName', "Column")
}
}
]
};
const tabs = [columnsTab, advancedTab];
if (designerInfo.view.addtionalTabs) {
tabs.push(...tabs);
}
this._view = {
components: [{
componentType: 'input',
propertyName: designers.TableColumnProperty.Name,
componentProperties: {
title: localize('tableDesigner.nameTitle', "Table name"),
width: 200
}
}],
tabs: tabs
};
}
private setDefaultData(): void {
const properties = Object.keys(this._data);
this.setDefaultInputData(properties, designers.TableProperty.Name);
this.setDefaultInputData(properties, designers.TableProperty.Schema);
this.setDefaultInputData(properties, designers.TableProperty.Description);
}
private setDefaultInputData(allProperties: string[], property: string): void {
if (allProperties.indexOf(property) === -1) {
this._data[property] = {};
}
}
}
@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TableDesignerProvider, ITableDesignerService } from 'sql/workbench/services/tableDesigner/common/interface';
import { invalidProvider } from 'sql/base/common/errors';
import * as azdata from 'azdata';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { TableDesignerInput } from 'sql/workbench/browser/editor/tableDesigner/tableDesignerInput';
export class TableDesignerService implements ITableDesignerService {
constructor(@IEditorService private _editorService: IEditorService) { }
public _serviceBrand: undefined;
private _providers = new Map<string, TableDesignerProvider>();
/**
* Register a data grid provider
*/
public registerProvider(providerId: string, provider: TableDesignerProvider): void {
if (this._providers.has(providerId)) {
throw new Error(`A table designer provider with id "${providerId}" is already registered`);
}
this._providers.set(providerId, provider);
}
public unregisterProvider(providerId: string): void {
this._providers.delete(providerId);
}
public getProvider(providerId: string): TableDesignerProvider {
const provider = this._providers.get(providerId);
if (provider) {
return provider;
}
throw invalidProvider(providerId);
}
public async openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo): Promise<void> {
const provider = this.getProvider(providerId);
const tableDesignerInput = new TableDesignerInput(provider, tableInfo);
await this._editorService.openEditor(tableDesignerInput, { pinned: true }, ACTIVE_GROUP);
}
}
@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const SERVICE_ID = 'tableDesignerService';
export const ITableDesignerService = createDecorator<ITableDesignerService>(SERVICE_ID);
export interface TableDesignerProvider extends azdata.designers.TableDesignerProvider { }
export interface ITableDesignerService {
_serviceBrand: undefined;
/**
* Register a table designer provider
*/
registerProvider(providerId: string, provider: TableDesignerProvider): void;
/**
* Unregister a table designer provider
*/
unregisterProvider(providerId: string): void;
/**
* Gets a registered table designer provider, throwing if none are registered with the specified ID
* @param providerId The id of the registered provider
*/
getProvider(providerId: string): TableDesignerProvider;
/**
* Open a table designer for the given table
* @param providerId The provider id
* @param tableInfo The table information
*/
openTableDesigner(providerId: string, tableInfo: azdata.designers.TableInfo): Promise<void>;
}