diff --git a/src/sql/base/parts/editableDropdown/browser/dropdown.ts b/src/sql/base/browser/ui/editableDropdown/browser/dropdown.ts similarity index 98% rename from src/sql/base/parts/editableDropdown/browser/dropdown.ts rename to src/sql/base/browser/ui/editableDropdown/browser/dropdown.ts index e6eb1439ac..61f1904e49 100644 --- a/src/sql/base/parts/editableDropdown/browser/dropdown.ts +++ b/src/sql/base/browser/ui/editableDropdown/browser/dropdown.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/dropdownList'; import { IInputBoxStyles, InputBox } from 'sql/base/browser/ui/inputBox/inputBox'; -import { DropdownDataSource, IDropdownListItem, DropdownListRenderer, SELECT_OPTION_ENTRY_TEMPLATE_ID } from 'sql/base/parts/editableDropdown/browser/dropdownList'; +import { DropdownDataSource, DropdownListRenderer, IDropdownListItem, SELECT_OPTION_ENTRY_TEMPLATE_ID } from 'sql/base/browser/ui/editableDropdown/browser/dropdownList'; import * as DOM from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; diff --git a/src/sql/base/parts/editableDropdown/browser/dropdownList.ts b/src/sql/base/browser/ui/editableDropdown/browser/dropdownList.ts similarity index 100% rename from src/sql/base/parts/editableDropdown/browser/dropdownList.ts rename to src/sql/base/browser/ui/editableDropdown/browser/dropdownList.ts diff --git a/src/sql/base/parts/editableDropdown/browser/media/dropdownList.css b/src/sql/base/browser/ui/editableDropdown/browser/media/dropdownList.css similarity index 100% rename from src/sql/base/parts/editableDropdown/browser/media/dropdownList.css rename to src/sql/base/browser/ui/editableDropdown/browser/media/dropdownList.css diff --git a/src/sql/base/browser/ui/table/tableCellEditorFactory.ts b/src/sql/base/browser/ui/table/tableCellEditorFactory.ts index 6ef0578788..0b12572b8b 100644 --- a/src/sql/base/browser/ui/table/tableCellEditorFactory.ts +++ b/src/sql/base/browser/ui/table/tableCellEditorFactory.ts @@ -9,12 +9,13 @@ 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'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; export interface ITableCellEditorOptions { valueGetter?: (item: Slick.SlickData, column: Slick.Column) => string, valueSetter?: (context: any, row: number, item: Slick.SlickData, column: Slick.Column, value: string) => void, optionsGetter?: (item: Slick.SlickData, column: Slick.Column) => string[], - editorStyler: (component: InputBox | SelectBox) => void + editorStyler: (component: InputBox | SelectBox | Dropdown) => void } export class TableCellEditorFactory { @@ -101,11 +102,12 @@ export class TableCellEditorFactory { return TextEditor; } - public getSelectBoxEditorClass(context: any, defaultOptions: string[]): any { + public getDropdownEditorClass(context: any, defaultOptions: string[], isEditable?: boolean): any { const self = this; class TextEditor { private _originalValue: string; private _selectBox: SelectBox; + private _dropdown: Dropdown; private _keyCaptureList: number[]; constructor(private _args: Slick.Editors.EditorOptions) { @@ -124,21 +126,37 @@ export class TableCellEditorFactory { 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(); + if (isEditable) { + this._dropdown = new Dropdown(container, self._contextViewProvider); + container.style.height = '100%'; + container.style.width = '100%'; + self._options.editorStyler(this._dropdown); + this._dropdown.focus(); + } else { + 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(); + if (isEditable) { + this._dropdown.dispose(); + } else { + this._selectBox.dispose(); + } } public focus(): void { - this._selectBox.focus(); + if (isEditable) { + this._dropdown.focus(); + } else { + this._selectBox.focus(); + } } public loadValue(item: Slick.SlickData): void { @@ -146,8 +164,13 @@ export class TableCellEditorFactory { 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); + if (isEditable) { + this._dropdown.values = options; + this._dropdown.value = options[idx]; + } else { + this._selectBox.setOptions(options); + this._selectBox.select(idx); + } } } @@ -157,12 +180,19 @@ export class TableCellEditorFactory { } public isValueChanged(): boolean { - return this._selectBox.value !== this._originalValue.toString(); - + if (isEditable) { + return this._dropdown.value !== this._originalValue.toString(); + } else { + return this._selectBox.value !== this._originalValue.toString(); + } } public serializeValue(): any { - return this._selectBox.value; + if (isEditable) { + return this._dropdown.value; + } else { + return this._selectBox.value; + } } public validate(): Slick.ValidateResults { diff --git a/src/sql/base/test/parts/editableDropdown/browser/dropdown.test.ts b/src/sql/base/test/parts/editableDropdown/browser/dropdown.test.ts index a5070cbe76..a8530ecf18 100644 --- a/src/sql/base/test/parts/editableDropdown/browser/dropdown.test.ts +++ b/src/sql/base/test/parts/editableDropdown/browser/dropdown.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown'; +import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; const options: IDropdownOptions = { values: [ diff --git a/src/sql/platform/browser/editableDropdown/editableDropdown.component.ts b/src/sql/platform/browser/editableDropdown/editableDropdown.component.ts index 9f43fa441c..addb7719a2 100644 --- a/src/sql/platform/browser/editableDropdown/editableDropdown.component.ts +++ b/src/sql/platform/browser/editableDropdown/editableDropdown.component.ts @@ -8,8 +8,8 @@ import { Output, OnChanges, SimpleChanges, EventEmitter } from '@angular/core'; -import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { AngularDisposable } from 'sql/base/browser/lifecycle'; +import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; import { attachEditableDropdownStyler } from 'sql/platform/theme/common/styler'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; diff --git a/src/sql/platform/theme/common/styler.ts b/src/sql/platform/theme/common/styler.ts index 7117c6c182..a3fe262b21 100644 --- a/src/sql/platform/theme/common/styler.ts +++ b/src/sql/platform/theme/common/styler.ts @@ -379,12 +379,14 @@ export function attachDesignerStyler(widget: any, themeService: IThemeService): const tableStyles = computeStyles(colorTheme, defaultTableStyles); const checkboxStyles = computeStyles(colorTheme, defaultCheckboxStyles); const buttonStyles = computeStyles(colorTheme, defaultButtonStyles); + const editableDropdownStyles = computeStyles(colorTheme, defaultEditableDropdownStyle); widget.style({ inputBoxStyles: inputStyles, selectBoxStyles: selectBoxStyles, tableStyles: tableStyles, checkboxStyles: checkboxStyles, buttonStyles: buttonStyles, + dropdownStyles: editableDropdownStyles, paneSeparator: cr.resolveColorValue(sqlcr.DesignerPaneSeparator, colorTheme), groupHeaderBackground: cr.resolveColorValue(sqlcr.GroupHeaderBackground, colorTheme) }); diff --git a/src/sql/workbench/browser/designer/designer.ts b/src/sql/workbench/browser/designer/designer.ts index fc1199be4f..f0993f6aba 100644 --- a/src/sql/workbench/browser/designer/designer.ts +++ b/src/sql/workbench/browser/designer/designer.ts @@ -42,6 +42,8 @@ import { DesignerPropertyPathValidator } from 'sql/workbench/browser/designer/de import { IThemeService } from 'vs/platform/theme/common/themeService'; import { listActiveSelectionBackground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { Dropdown, IDropdownStyles } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; +import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; export interface IDesignerStyle { tabbedPanelStyles?: ITabbedPanelStyles; @@ -50,11 +52,12 @@ export interface IDesignerStyle { selectBoxStyles?: ISelectBoxStyles; checkboxStyles?: ICheckboxStyles; buttonStyles?: IButtonStyles; + dropdownStyles?: IListStyles & IInputBoxStyles & IDropdownStyles; paneSeparator?: Color; groupHeaderBackground?: Color; } -export type DesignerUIComponent = InputBox | Checkbox | Table | SelectBox; +export type DesignerUIComponent = InputBox | Checkbox | Table | SelectBox | Dropdown; export type CreateComponentsFunc = (container: HTMLElement, components: DesignerDataPropertyInfo[], parentPath: DesignerPropertyPath) => DesignerUIComponent[]; export type SetComponentValueFunc = (definition: DesignerDataPropertyInfo, component: DesignerUIComponent, data: DesignerViewModel) => void; @@ -191,7 +194,7 @@ export class Designer extends Disposable implements IThemable { }); } - private styleComponent(component: TabbedPanel | InputBox | Checkbox | Table | SelectBox | Button): void { + private styleComponent(component: TabbedPanel | InputBox | Checkbox | Table | SelectBox | Button | Dropdown): void { if (component instanceof InputBox) { component.style(this._styles.inputBoxStyles); } else if (component instanceof Checkbox) { @@ -202,6 +205,8 @@ export class Designer extends Disposable implements IThemable { component.style(this._styles.tableStyles); } else if (component instanceof Button) { component.style(this._styles.buttonStyles); + } else if (component instanceof Dropdown) { + component.style(this._styles.dropdownStyles); } else { component.style(this._styles.selectBoxStyles); } @@ -607,19 +612,33 @@ export class Designer extends Disposable implements IThemable { checkbox.checked = checkboxData.checked; break; case 'dropdown': - const dropdown = component as SelectBox; - const defaultDropdownData = definition.componentProperties as DropDownProperties; + const dropdownProperties = definition.componentProperties as DropDownProperties; const dropdownData = viewModel[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 options = (dropdownData.values || dropdownProperties.values || []) as string[]; const idx = options?.indexOf(dropdownData.value as string); - if (idx > -1) { - dropdown.select(idx); + let dropdown: Dropdown | SelectBox; + if (dropdownProperties.isEditable) { + dropdown = component as Dropdown; + if (dropdownData.enabled === false) { + dropdown.enabled = false; + } else { + dropdown.enabled = true; + } + dropdown.values = options; + if (idx > -1) { + dropdown.value = options[idx]; + } + } else { + dropdown = component as SelectBox; + if (dropdownData.enabled === false) { + dropdown.disable(); + } else { + dropdown.enable(); + } + dropdown.setOptions(options); + if (idx > -1) { + dropdown.select(idx); + } } break; default: @@ -703,20 +722,36 @@ export class Designer extends Disposable implements IThemable { container.appendChild(DOM.$('')).appendChild(DOM.$('span.component-label')).innerText = componentDefinition.componentProperties?.title ?? ''; const dropdownContainer = container.appendChild(DOM.$('')); const dropdownProperties = componentDefinition.componentProperties as DropDownProperties; - const dropdown = new SelectBox(dropdownProperties.values as string[] || [], undefined, this._contextViewProvider, undefined); - dropdown.setAriaLabel(componentDefinition.componentProperties?.title); - dropdown.render(dropdownContainer); - dropdown.selectElem.style.height = '25px'; - dropdown.onDidSelect((e) => { - this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: e.selected }); - }); - dropdown.onDidFocus(() => { - if (view === 'PropertiesView') { - this._propertiesPane.updateDescription(componentDefinition); - } else if (view === 'TabsView' || view === 'TopContentView') { - this.updatePropertiesPane(DesignerRootObjectPath); - } - }); + let dropdown; + if (dropdownProperties.isEditable) { + dropdown = new Dropdown(dropdownContainer, this._contextViewProvider, { values: dropdownProperties.values as string[] || [] }); + dropdown.ariaLabel = componentDefinition.componentProperties?.title; + dropdown.onValueChange((value) => { + this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: value }); + }); + dropdown.onFocus(() => { + if (view === 'PropertiesView') { + this._propertiesPane.updateDescription(componentDefinition); + } else if (view === 'TabsView' || view === 'TopContentView') { + this.updatePropertiesPane(DesignerRootObjectPath); + } + }); + } else { + dropdown = new SelectBox(dropdownProperties.values as string[] || [], undefined, this._contextViewProvider, undefined); + dropdown.setAriaLabel(componentDefinition.componentProperties?.title); + dropdown.render(dropdownContainer); + dropdown.selectElem.style.height = '25px'; + dropdown.onDidSelect((e) => { + this.handleEdit({ type: DesignerEditType.Update, path: propertyPath, value: e.selected }); + }); + dropdown.onDidFocus(() => { + if (view === 'PropertiesView') { + this._propertiesPane.updateDescription(componentDefinition); + } else if (view === 'TabsView' || view === 'TopContentView') { + this.updatePropertiesPane(DesignerRootObjectPath); + } + }); + } component = dropdown; break; case 'checkbox': @@ -803,7 +838,7 @@ export class Designer extends Disposable implements IThemable { return { name: dropdownProperties.title, field: propertyDefinition.propertyName, - editor: this._tableCellEditorFactory.getSelectBoxEditorClass(propertyPath, dropdownProperties.values as string[]), + editor: this._tableCellEditorFactory.getDropdownEditorClass(propertyPath, dropdownProperties.values as string[], dropdownProperties.isEditable), width: dropdownProperties.width as number }; default: diff --git a/src/sql/workbench/browser/designer/interfaces.ts b/src/sql/workbench/browser/designer/interfaces.ts index 65b3eccf39..9744e5e3b1 100644 --- a/src/sql/workbench/browser/designer/interfaces.ts +++ b/src/sql/workbench/browser/designer/interfaces.ts @@ -149,6 +149,7 @@ export interface CategoryValue { export interface DropDownProperties extends ComponentProperties { value?: string | CategoryValue; values?: string[] | CategoryValue[]; + isEditable?: boolean; } export interface CheckBoxProperties extends ComponentProperties { diff --git a/src/sql/workbench/browser/modelComponents/dropdown.component.ts b/src/sql/workbench/browser/modelComponents/dropdown.component.ts index 676c3f72f1..d74d7b9297 100644 --- a/src/sql/workbench/browser/modelComponents/dropdown.component.ts +++ b/src/sql/workbench/browser/modelComponents/dropdown.component.ts @@ -12,7 +12,6 @@ import { import * as azdata from 'azdata'; import { ComponentBase } from 'sql/workbench/browser/modelComponents/componentBase'; -import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; import { attachEditableDropdownStyler } from 'sql/platform/theme/common/styler'; import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; @@ -26,6 +25,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { ILogService } from 'vs/platform/log/common/log'; import { registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { errorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry'; +import { Dropdown, IDropdownOptions } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; @Component({ selector: 'modelview-dropdown', diff --git a/src/sql/workbench/contrib/query/browser/queryActions.ts b/src/sql/workbench/contrib/query/browser/queryActions.ts index 3623616026..b718e3f519 100644 --- a/src/sql/workbench/contrib/query/browser/queryActions.ts +++ b/src/sql/workbench/contrib/query/browser/queryActions.ts @@ -27,7 +27,6 @@ import { import { QueryEditor } from 'sql/workbench/contrib/query/browser/queryEditor'; import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel'; import { attachEditableDropdownStyler } from 'sql/platform/theme/common/styler'; -import { Dropdown } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { Task } from 'sql/workbench/services/tasks/browser/tasksRegistry'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -46,6 +45,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { getErrorMessage, onUnexpectedError } from 'vs/base/common/errors'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { gen3Version, sqlDataWarehouse } from 'sql/platform/connection/common/constants'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; /** * Action class that query-based Actions will extend. This base class automatically handles activating and diff --git a/src/sql/workbench/services/connection/browser/connectionWidget.ts b/src/sql/workbench/services/connection/browser/connectionWidget.ts index 9f50c94b06..43ac51e68a 100644 --- a/src/sql/workbench/services/connection/browser/connectionWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionWidget.ts @@ -14,7 +14,6 @@ import { IConnectionComponentCallbacks } from 'sql/workbench/services/connection import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; -import { Dropdown } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; @@ -34,6 +33,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; export enum AuthenticationType { SqlLogin = 'SqlLogin', diff --git a/src/sql/workbench/services/restore/browser/restoreDialog.ts b/src/sql/workbench/services/restore/browser/restoreDialog.ts index dc97dc90e1..47b29dabdf 100644 --- a/src/sql/workbench/services/restore/browser/restoreDialog.ts +++ b/src/sql/workbench/services/restore/browser/restoreDialog.ts @@ -34,7 +34,6 @@ import { attachTableStyler, attachInputBoxStyler, attachSelectBoxStyler, attachE import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; import { RestoreViewModel, RestoreOptionParam, SouceDatabaseNamesParam } from 'sql/workbench/services/restore/browser/restoreViewModel'; import * as FileValidationConstants from 'sql/workbench/services/fileBrowser/common/fileValidationServiceConstants'; -import { Dropdown } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel'; import { ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService'; @@ -46,6 +45,7 @@ import { attachModalDialogStyler, attachTabbedPanelStyler } from 'sql/workbench/ import { fileFiltersSet } from 'sql/workbench/services/restore/common/constants'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { Dropdown } from 'sql/base/browser/ui/editableDropdown/browser/dropdown'; interface FileListElement { logicalFileName: string;