diff --git a/src/sql/base/browser/ui/checkbox/checkbox.ts b/src/sql/base/browser/ui/checkbox/checkbox.ts index f4efa3b252..a45d7c178e 100644 --- a/src/sql/base/browser/ui/checkbox/checkbox.ts +++ b/src/sql/base/browser/ui/checkbox/checkbox.ts @@ -30,6 +30,9 @@ export class Checkbox extends Widget { private _onChange = new Emitter(); public readonly onChange: Event = this._onChange.event; + private _onFocus = new Emitter(); + public readonly onFocus: Event = this._onFocus.event; + constructor(container: HTMLElement, opts: ICheckboxOptions) { super(); const id = generateUuid(); @@ -46,6 +49,9 @@ export class Checkbox extends Widget { this._onChange.fire(this.checked); }); + this.onfocus(this._el, () => { + this._onFocus.fire(); + }); this._label = document.createElement('label'); this._label.style.verticalAlign = 'middle'; diff --git a/src/sql/base/browser/ui/inputBox/inputBox.ts b/src/sql/base/browser/ui/inputBox/inputBox.ts index 14562abace..4b4810f06d 100644 --- a/src/sql/base/browser/ui/inputBox/inputBox.ts +++ b/src/sql/base/browser/ui/inputBox/inputBox.ts @@ -41,6 +41,9 @@ export class InputBox extends vsInputBox { private _onLoseFocus = this._register(new Emitter()); public onLoseFocus: Event = this._onLoseFocus.event; + private _onInputFocus = this._register(new Emitter()); + public onInputFocus: Event = this._onInputFocus.event; + private _isTextAreaInput = false; private _hideErrors = false; @@ -58,6 +61,10 @@ export class InputBox extends vsInputBox { self._lastLoseFocusValue = self.value; }); + this.onfocus(this.inputElement, () => { + self._onInputFocus.fire(); + }); + if (_sqlOptions && _sqlOptions.type === 'textarea') { this._isTextAreaInput = true; } diff --git a/src/sql/base/browser/ui/selectBox/selectBox.ts b/src/sql/base/browser/ui/selectBox/selectBox.ts index b69f38637b..f30872c363 100644 --- a/src/sql/base/browser/ui/selectBox/selectBox.ts +++ b/src/sql/base/browser/ui/selectBox/selectBox.ts @@ -53,6 +53,7 @@ export class SelectBox extends vsSelectBox { private contextViewProvider: IContextViewProvider; private message?: IMessage; private _onDidSelect: Emitter; + private _onDidFocus: Emitter; private inputValidationInfoBorder?: Color; private inputValidationInfoBackground?: Color; @@ -71,6 +72,7 @@ export class SelectBox extends vsSelectBox { super(optionItems, 0, contextViewProvider, undefined, selectBoxOptions); this._onDidSelect = new Emitter(); + this._onDidFocus = new Emitter(); this._optionsDictionary = new Map(); this.populateOptionsDictionary(optionItems); this._dialogOptions = optionItems; @@ -100,7 +102,10 @@ export class SelectBox extends vsSelectBox { let focusTracker = dom.trackFocus(this.selectElement); this._register(focusTracker); this._register(focusTracker.onDidBlur(() => this._hideMessage())); - this._register(focusTracker.onDidFocus(() => this._showMessage())); + this._register(focusTracker.onDidFocus(() => { + this._showMessage(); + this._onDidFocus.fire(); + })); // Stop propagation - we've handled the event already and letting it bubble up causes issues with parent // controls handling it (such as dialog pages) this.onkeydown(this.selectElement, (e: IKeyboardEvent) => { @@ -133,6 +138,10 @@ export class SelectBox extends vsSelectBox { return this._onDidSelect.event; } + public get onDidFocus(): Event { + return this._onDidFocus.event; + } + public onSelect(newInput: ISelectData) { const selected = this._dialogOptions[newInput.index]; this._selectedOption = selected.value; diff --git a/src/sql/workbench/browser/designer/designer.ts b/src/sql/workbench/browser/designer/designer.ts index 92924251de..bfc6731171 100644 --- a/src/sql/workbench/browser/designer/designer.ts +++ b/src/sql/workbench/browser/designer/designer.ts @@ -551,6 +551,11 @@ export class Designer extends Disposable implements IThemable { this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: args.value }); } }); + input.onInputFocus(() => { + if (!isMainView) { + this._propertiesPane.updateDescription(componentDefinition); + } + }); if (setWidth && inputProperties.width !== undefined) { input.width = inputProperties.width as number; } @@ -566,6 +571,11 @@ export class Designer extends Disposable implements IThemable { dropdown.onDidSelect((e) => { this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: e.selected }); }); + dropdown.onDidFocus(() => { + if (!isMainView) { + this._propertiesPane.updateDescription(componentDefinition); + } + }); component = dropdown; break; case 'checkbox': @@ -578,6 +588,11 @@ export class Designer extends Disposable implements IThemable { checkbox.onChange((newValue) => { this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: newValue }); }); + checkbox.onFocus(() => { + if (!isMainView) { + this._propertiesPane.updateDescription(componentDefinition); + } + }); component = checkbox; break; case 'table': diff --git a/src/sql/workbench/browser/designer/designerPropertiesPane.ts b/src/sql/workbench/browser/designer/designerPropertiesPane.ts index 2d3e54b2f7..c44d4164d0 100644 --- a/src/sql/workbench/browser/designer/designerPropertiesPane.ts +++ b/src/sql/workbench/browser/designer/designerPropertiesPane.ts @@ -23,11 +23,17 @@ export class DesignerPropertiesPane { private _componentMap = new Map(); private _groupHeaders: HTMLElement[] = []; + // Description variables + private _descriptionContainer: HTMLElement; + private _descriptionTitleContainer: HTMLElement; + private _descriptionTextContainer: HTMLElement; + constructor(container: HTMLElement, private _createComponents: CreateComponentsFunc, private _setComponentValue: SetComponentValueFunc) { 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"); + this.createDescriptionComponent(container); } public get groupHeaders(): HTMLElement[] { @@ -42,6 +48,15 @@ export class DesignerPropertiesPane { return this._objectPath; } + public updateDescription(definition: DesignerDataPropertyInfo) { + const title: string = definition.componentProperties.title; + const description: string = definition.description; + if (title && description) { + this._descriptionTitleContainer.innerText = title; + this._descriptionTextContainer.innerText = description; + } + } + public clear(): void { this._componentMap.forEach((value) => { value.component.dispose(); @@ -52,6 +67,15 @@ export class DesignerPropertiesPane { this._objectPath = undefined; } + private createDescriptionComponent(container: HTMLElement) { + this._descriptionContainer = container.appendChild(DOM.$('.description-component')); + this._descriptionTitleContainer = this._descriptionContainer.appendChild(DOM.$('')).appendChild(DOM.$('.description-component-label')); + this._descriptionTitleContainer.classList.add('codicon', 'info'); + this._descriptionTextContainer = this._descriptionContainer.appendChild(DOM.$('.description-component-content')); + this._descriptionTitleContainer.innerText = ''; + this._descriptionTextContainer.innerText = ''; + } + public show(item: ObjectInfo): void { if (!equals(item.path, this._objectPath)) { this.clear(); diff --git a/src/sql/workbench/browser/designer/media/designer.css b/src/sql/workbench/browser/designer/media/designer.css index 2292beca9c..cadfc49bc2 100644 --- a/src/sql/workbench/browser/designer/media/designer.css +++ b/src/sql/workbench/browser/designer/media/designer.css @@ -102,3 +102,25 @@ font-weight: bold; line-height: 25px; } + +.designer-component .description-component { + margin: 15px; + height: 90px; + overflow-y: auto; +} + +.designer-component .description-component-label.codicon.info { + background-repeat: no-repeat; + background-position: 2px center; + padding-left: 25px; + background-size: 16px; + font-size: 15px; + font-weight: 600; + margin-block-start: 0px; + scroll-margin-block-end: 0px; +} + +.designer-component .description-component-content { + padding-top: 10px; + padding-left: 25px; +} diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts index a1d757f2eb..d607005b30 100644 --- a/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerComponentInput.ts @@ -10,6 +10,7 @@ import { localize } from 'vs/nls'; import { designers } from 'sql/workbench/api/common/sqlExtHostTypes'; import { Emitter, Event } from 'vs/base/common/event'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { ColumnPropertyDescriptions, TablePropertyDescriptions } from 'sql/workbench/services/tableDesigner/browser/tableDesignerStrings'; import { deepClone, equals } from 'vs/base/common/objects'; export class TableDesignerComponentInput implements DesignerComponentInput { @@ -149,6 +150,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { { componentType: 'dropdown', propertyName: designers.TableProperty.Schema, + description: TablePropertyDescriptions.SCHEMA, componentProperties: { title: localize('tableDesigner.schemaTitle', "Schema"), values: designerInfo.schemas @@ -156,6 +158,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'input', propertyName: designers.TableProperty.Description, + description: TablePropertyDescriptions.DESCRIPTION, componentProperties: { title: localize('tableDesigner.descriptionTitle', "Description") } @@ -175,6 +178,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { { componentType: 'input', propertyName: designers.TableColumnProperty.Name, + description: ColumnPropertyDescriptions.NAME, componentProperties: { title: localize('tableDesigner.columnNameTitle', "Name"), width: 150 @@ -182,6 +186,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'dropdown', propertyName: designers.TableColumnProperty.Type, + description: ColumnPropertyDescriptions.DATA_TYPE, componentProperties: { title: localize('tableDesigner.columnTypeTitle', "Type"), width: 100, @@ -190,6 +195,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'input', propertyName: designers.TableColumnProperty.Length, + description: ColumnPropertyDescriptions.LENGTH, componentProperties: { title: localize('tableDesigner.columnLengthTitle', "Length"), width: 60 @@ -197,6 +203,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'input', propertyName: designers.TableColumnProperty.DefaultValue, + description: ColumnPropertyDescriptions.DEFAULT_VALUE_OR_BINDING, componentProperties: { title: localize('tableDesigner.columnDefaultValueTitle', "Default Value"), width: 150 @@ -204,18 +211,21 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'checkbox', propertyName: designers.TableColumnProperty.AllowNulls, + description: ColumnPropertyDescriptions.ALLOW_NULLS, componentProperties: { title: localize('tableDesigner.columnAllowNullTitle', "Allow Nulls"), } }, { componentType: 'checkbox', propertyName: designers.TableColumnProperty.IsPrimaryKey, + description: ColumnPropertyDescriptions.PRIMARY_KEY, componentProperties: { title: localize('tableDesigner.columnIsPrimaryKeyTitle', "Primary Key"), } }, { componentType: 'input', propertyName: designers.TableColumnProperty.Precision, + description: ColumnPropertyDescriptions.PRECISION, componentProperties: { title: localize('tableDesigner.columnPrecisionTitle', "Precision"), width: 60 @@ -223,6 +233,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { }, { componentType: 'input', propertyName: designers.TableColumnProperty.Scale, + description: ColumnPropertyDescriptions.NAME, componentProperties: { title: localize('tableDesigner.columnScaleTitle', "Scale"), width: 60 @@ -273,6 +284,7 @@ export class TableDesignerComponentInput implements DesignerComponentInput { components: [{ componentType: 'input', propertyName: designers.TableColumnProperty.Name, + description: TablePropertyDescriptions.NAME, componentProperties: { title: localize('tableDesigner.nameTitle', "Table name"), width: 200 diff --git a/src/sql/workbench/services/tableDesigner/browser/tableDesignerStrings.ts b/src/sql/workbench/services/tableDesigner/browser/tableDesignerStrings.ts new file mode 100644 index 0000000000..9a9bacd553 --- /dev/null +++ b/src/sql/workbench/services/tableDesigner/browser/tableDesignerStrings.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export const TablePropertyDescriptions = { + NAME: localize('designer.table.description.name', "The name of the table object."), + DESCRIPTION: localize('designer.table.description.description', "Description for the table."), + SCHEMA: localize('designer.table.description.schema', "The schema that contains the table.") +}; + +export const ColumnPropertyDescriptions = { + NAME: localize('designer.column.description.name', "The name of the column object."), + ALLOW_NULLS: localize('designer.column.description.allowNulls', "Specifies whether the column may have a NULL value."), + DATA_TYPE: localize('designer.column.description.dataType', "Displays the data type name for the column"), + DEFAULT_VALUE_OR_BINDING: localize('designer.column.description.defaultValueBinding', "A predefined global default value for the column or binding."), + DESCRIPTION: localize('designer.column.description.description', "Description for the column."), + LENGTH: localize('designer.column.description.length', "The maximum length (in characters) that can be stored in this database object."), + PRECISION: localize('designer.column.description.precision', "For numeric data, the maximum number of decimal digits that can be stored in this database object."), + PRIMARY_KEY: localize('designer.column.description.primaryKey', "Specifies whether the column is included in the primary key for the table.") +};