Refresh master with initial release/0.24 snapshot (#332)

* Initial port of release/0.24 source code

* Fix additional headers

* Fix a typo in launch.json
This commit is contained in:
Karl Burtram
2017-12-15 15:38:57 -08:00
committed by GitHub
parent 271b3a0b82
commit 6ad0df0e3e
7118 changed files with 107999 additions and 56466 deletions

View File

@@ -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.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Button as vsButton, IButtonOptions, IButtonStyles as vsIButtonStyles } from 'vs/base/browser/ui/button/button';
import * as DOM from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
export interface IButtonStyles extends vsIButtonStyles {
buttonFocusOutline?: Color;
}
export class Button extends vsButton {
private buttonFocusOutline: Color;
constructor(container: any, options?: IButtonOptions) {
super(container, options);
this.buttonFocusOutline = null;
this.$el.on(DOM.EventType.FOCUS, (e) => {
this.$el.style('outline-color', this.buttonFocusOutline ? this.buttonFocusOutline.toString() : null);
this.$el.style('outline-width', '1px');
});
}
public style(styles: IButtonStyles): void {
super.style(styles);
this.buttonFocusOutline = styles.buttonFocusOutline;
}
public set title(value: string) {
this.$el.title(value);
}
}

View File

@@ -2,53 +2,85 @@
* 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!sql/base/browser/ui/checkbox/media/checkbox';
import { Checkbox as vsCheckbox, ICheckboxOpts, ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
import { Color } from 'vs/base/common/color';
const defaultOpts = {
inputActiveOptionBorder: Color.fromHex('#007ACC'),
actionClassName: ' sql-checkbox'
};
import Event, { Emitter } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Widget } from 'vs/base/browser/ui/widget';
/**
* Extends Checkbox to include Carbon checkbox icon and styling.
*/
export class Checkbox extends vsCheckbox {
private _inputActiveOptionBorder: Color;
export interface ICheckboxOptions {
label: string;
enabled?: boolean;
checked?: boolean;
onChange?: (val: boolean) => void;
}
constructor(opts: ICheckboxOpts) {
super({
actionClassName: opts.actionClassName + defaultOpts.actionClassName,
title: opts.title,
isChecked: opts.isChecked,
onChange: opts.onChange,
onKeyDown: opts.onKeyDown,
inputActiveOptionBorder: opts.inputActiveOptionBorder
export class Checkbox extends Widget {
private _el: HTMLInputElement;
private _label: HTMLSpanElement;
private _onChange = new Emitter<boolean>();
public readonly onChange: Event<boolean> = this._onChange.event;
constructor(container: HTMLElement, opts: ICheckboxOptions) {
super();
this._el = document.createElement('input');
this._el.type = 'checkbox';
this.onchange(this._el, e => {
this._onChange.fire(this.checked);
});
this._inputActiveOptionBorder = opts.inputActiveOptionBorder ? opts.inputActiveOptionBorder : defaultOpts.inputActiveOptionBorder;
this.onkeydown(this._el, e => {
if (e.equals(KeyCode.Enter)) {
this.checked = !this.checked;
e.stopPropagation();
}
});
this._label = document.createElement('span');
this.label = opts.label;
this.enabled = opts.enabled || true;
this.checked = opts.checked || false;
if (opts.onChange) {
this.onChange(opts.onChange);
}
container.appendChild(this._el);
container.appendChild(this._label);
}
public enable(): void {
super.enable();
this.domNode.classList.remove('disabled');
public set label(val: string) {
this._label.innerText = val;
}
public set enabled(val: boolean) {
this._el.disabled = !val;
}
public get enabled(): boolean {
return !this._el.disabled;
}
public set checked(val: boolean) {
this._el.checked = val;
}
public get checked(): boolean {
return this._el.checked;
}
public focus(): void {
this._el.focus();
}
public disable(): void {
super.disable();
this.domNode.classList.add('disabled');
this.enabled = false;
}
public style(styles: ICheckboxStyles): void {
if (styles.inputActiveOptionBorder) {
this._inputActiveOptionBorder = styles.inputActiveOptionBorder;
}
this.applyStyles();
public enable(): void {
this.enabled = true;
}
protected applyStyles(): void {
if (this.domNode) {
this.domNode.style.borderColor = this._inputActiveOptionBorder ? this._inputActiveOptionBorder.toString(): defaultOpts.inputActiveOptionBorder.toString();
}
}
}
}

View File

@@ -1,71 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Event, { Emitter } from 'vs/base/common/event';
import * as DOM from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export interface ICheckboxOptions {
label: string;
enabled?: boolean;
checked?: boolean;
}
export class Checkbox extends Disposable {
private _el: HTMLInputElement;
private _label: HTMLSpanElement;
private _onChange = new Emitter<boolean>();
public readonly onChange: Event<boolean> = this._onChange.event;
constructor(container: HTMLElement, opts: ICheckboxOptions) {
super();
this._el = document.createElement('input');
this._el.type = 'checkbox';
this._register(DOM.addDisposableListener(this._el, DOM.EventType.CHANGE, e => {
this._onChange.fire(e);
}));
this._register(DOM.addStandardDisposableListener(this._el, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
if (e.equals(KeyCode.Enter)) {
this.checked = !this.checked;
e.stopPropagation();
}
}));
this._label = document.createElement('span');
this.label = opts.label;
this.enabled = opts.enabled;
this.checked = opts.checked;
container.appendChild(this._el);
container.appendChild(this._label);
}
public set label(val: string) {
this._label.innerText = val;
}
public set enabled(val: boolean) {
this._el.disabled = !val;
}
public get enabled(): boolean {
return !this._el.disabled;
}
public set checked(val: boolean) {
this._el.checked = val;
}
public get checked(): boolean {
return this._el.checked;
}
}

View File

@@ -11,13 +11,14 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import { IAction } from 'vs/base/common/actions';
import { Button } from 'vs/base/browser/ui/button/button';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { EventType as GestureEventType } from 'vs/base/browser/touch';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Button } from 'sql/base/browser/ui/button/button';
import { attachButtonStyler } from 'sql/common/theme/styler';
export interface IDropdownStyles {
backgroundColor?: Color;
foregroundColor?: Color;
@@ -46,6 +47,14 @@ export class DropdownList extends Dropdown {
this._action.run();
this.hide();
}));
this.toDispose.push(DOM.addDisposableListener(button.getElement(), DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
e.stopPropagation();
this._action.run();
this.hide();
}
}));
attachButtonStyler(button, this._themeService);
}

View File

@@ -6,15 +6,15 @@
import 'vs/css!./media/dropdownList';
import { ToggleDropdownAction } from './actions';
import { DropdownDataSource, DropdownFilter, DropdownModel, DropdownRenderer, DropdownController } from './dropdownTree';
import { IContextViewProvider, ContextView } from 'vs/base/browser/ui/contextview/contextview';
import { mixin } from 'vs/base/common/objects';
import { Builder, $ } from 'vs/base/browser/builder';
import { InputBox, IInputBoxStyles } from 'sql/base/browser/ui/inputBox/inputBox';
import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { List, IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom';
import { IDelegate, IRenderer } from 'vs/base/browser/ui/list/list';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
@@ -22,6 +22,7 @@ import * as nls from 'vs/nls';
import Event, { Emitter } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
export interface IDropdownOptions extends IDropdownStyles {
/**
@@ -72,47 +73,20 @@ interface TableTemplate {
label: HTMLElement;
}
class Delegate implements IDelegate<ListResource> {
getHeight = (): number => 22;
getTemplateId(element: ListResource): string {
return 'string';
}
}
class Renderer implements IRenderer<ListResource, TableTemplate> {
static TEMPLATE_ID = 'string';
get templateId(): string { return Renderer.TEMPLATE_ID; }
renderTemplate(container: HTMLElement): TableTemplate {
const row = $('div.list-row').style('height', '22px').style('padding-left', '5px').getHTMLElement();
DOM.append(container, row);
const label = $('span.label').style('margin', 'auto').getHTMLElement();
DOM.append(row, label);
return { label };
}
renderElement(resource: ListResource, index: number, template: TableTemplate): void {
template.label.innerText = resource.label;
}
disposeTemplate(template: TableTemplate): void {
// noop
}
}
export class Dropdown extends Disposable {
private $el: Builder;
private $input: Builder;
private $list: Builder;
private $treeContainer: Builder;
private _input: InputBox;
private _list: List<ListResource>;
private _values: string[];
private _tree: Tree;
private _options: IDropdownOptions;
private _toggleAction: ToggleDropdownAction;
// we have to create our own contextview since otherwise inputbox will override ours
private _contextView: ContextView;
private _dataSource = new DropdownDataSource();
private _filter = new DropdownFilter();
private _renderer = new DropdownRenderer();
private _controller = new DropdownController();
private _onBlur = this._register(new Emitter<void>());
public onBlur: Event<void> = this._onBlur.event;
@@ -132,13 +106,16 @@ export class Dropdown extends Disposable {
super();
this._contextView = new ContextView(document.body);
this._options = mixin(opt, defaults, false) as IDropdownOptions;
this._values = this._options.values;
this.$el = $('.dropdown').style('width', '100%').appendTo(container);
this.$input = $('.dropdown-input').style('width', '100%').appendTo(this.$el);
this.$list = $('.dropdown-list');
this.$treeContainer = $('.dropdown-tree');
this._toggleAction = new ToggleDropdownAction(() => this._showList());
this._toggleAction = new ToggleDropdownAction(() => {
this._showList();
this._tree.DOMFocus();
this._tree.focusFirst();
});
this._input = new InputBox(this.$input.getHTMLElement(), contextViewService, {
validationOptions: {
@@ -154,12 +131,12 @@ export class Dropdown extends Disposable {
}));
this._register(DOM.addDisposableListener(this._input.inputElement, DOM.EventType.BLUR, () => {
if (!this._list.isDOMFocused) {
if (!this._tree.isDOMFocused()) {
this._onBlur.fire();
}
}));
this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_UP, (e: StandardKeyboardEvent) => {
this._register(DOM.addStandardDisposableListener(this._input.inputElement, DOM.EventType.KEY_DOWN, (e: StandardKeyboardEvent) => {
switch (e.keyCode) {
case KeyCode.Enter:
if (this._input.validate()) {
@@ -168,7 +145,7 @@ export class Dropdown extends Disposable {
e.stopPropagation();
break;
case KeyCode.Escape:
if (this.$list.getHTMLElement().parentElement) {
if (this.$treeContainer.getHTMLElement().parentElement) {
this._input.validate();
this._onBlur.fire();
this._contextView.hide();
@@ -182,44 +159,55 @@ export class Dropdown extends Disposable {
e.stopPropagation();
break;
case KeyCode.DownArrow:
if (!this.$list.getHTMLElement().parentElement) {
if (!this.$treeContainer.getHTMLElement().parentElement) {
this._showList();
}
this._list.getHTMLElement().focus();
this._tree.DOMFocus();
this._tree.focusFirst();
e.stopPropagation();
e.preventDefault();
break;
}
}));
this._list = new List(this.$list.getHTMLElement(), new Delegate(), [new Renderer()]);
if (this._values) {
this._list.splice(0, this._list.length, this._values.map(i => { return { label: i }; }));
let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22;
this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px');
}
this._tree = new Tree(this.$treeContainer.getHTMLElement(), {
dataSource: this._dataSource,
filter: this._filter,
renderer: this._renderer,
controller: this._controller
});
this._list.onSelectionChange(e => {
if (e.elements.length === 1) {
this.value = e.elements[0].label;
this._onValueChange.fire(e.elements[0].label);
this._contextView.hide();
}
this.values = this._options.values;
this._controller.onSelectionChange(e => {
this.value = e.value;
this._onValueChange.fire(e.value);
this._input.focus();
this._contextView.hide();
});
this._input.onDidChange(e => {
if (this._values) {
this._list.splice(0, this._list.length, this._values.filter(i => i.includes(e)).map(i => { return { label: i }; }));
let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22;
this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px');
this._list.layout(parseInt(this.$list.style('height')));
if (this._dataSource.options) {
this._filter.filterString = e;
let filteredLength = this._dataSource.options.reduce((p, i) => {
if (this._filter.isVisible(undefined, i)) {
return p + 1;
} else {
return p;
}
}, 0);
let height = filteredLength * this._renderer.getHeight(undefined, undefined) > this._options.maxHeight ? this._options.maxHeight : filteredLength * this._renderer.getHeight(undefined, undefined);
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px');
this._tree.layout(parseInt(this.$treeContainer.style('height')));
this._tree.refresh();
}
});
this._register(this._contextView);
this._register(this.$el);
this._register(this.$input);
this._register(this.$list);
this._register(this._list);
this._register(this.$treeContainer);
this._register(this._tree);
this._register(this._input);
this._register(this._contextView);
}
@@ -230,12 +218,12 @@ export class Dropdown extends Disposable {
this._contextView.show({
getAnchor: () => this.$input.getHTMLElement(),
render: container => {
this.$list.appendTo(container);
this._list.layout(parseInt(this.$list.style('height')));
this.$treeContainer.appendTo(container);
this._tree.layout(parseInt(this.$treeContainer.style('height')));
return { dispose: () => { } };
},
onDOMEvent: (e, activeElement) => {
if (!DOM.isAncestor(activeElement, this.$el.getHTMLElement())) {
if (!DOM.isAncestor(activeElement, this.$el.getHTMLElement()) && !DOM.isAncestor(activeElement, this.$treeContainer.getHTMLElement())) {
this._input.validate();
this._onBlur.fire();
this._contextView.hide();
@@ -246,12 +234,15 @@ export class Dropdown extends Disposable {
}
public set values(vals: string[]) {
this._values = vals;
this._list.splice(0, this._list.length, this._values.map(i => { return { label: i }; }));
let height = this._list.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._list.length * 22;
this.$list.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px');
this._list.layout(parseInt(this.$list.style('height')));
this._input.validate();
if (vals) {
this._filter.filterString = '';
this._dataSource.options = vals.map(i => { return { value: i }; });
let height = this._dataSource.options.length * 22 > this._options.maxHeight ? this._options.maxHeight : this._dataSource.options.length * 22;
this.$treeContainer.style('height', height + 'px').style('width', DOM.getContentWidth(this.$input.getHTMLElement()) + 'px');
this._tree.layout(parseInt(this.$treeContainer.style('height')));
this._tree.setInput(new DropdownModel());
this._input.validate();
}
}
public get value(): string {
@@ -272,14 +263,14 @@ export class Dropdown extends Disposable {
}
style(style: IListStyles & IInputBoxStyles & IDropdownStyles) {
this._list.style(style);
this._tree.style(style);
this._input.style(style);
this.$list.style('background-color', style.contextBackground.toString());
this.$list.style('outline', `1px solid ${style.contextBorder || this._options.contextBorder}`);
this.$treeContainer.style('background-color', style.contextBackground.toString());
this.$treeContainer.style('outline', `1px solid ${style.contextBorder || this._options.contextBorder}`);
}
private _inputValidator(value: string): IMessage {
if (this._values && !this._values.includes(value)) {
if (this._dataSource.options && !this._dataSource.options.find(i => i.value === value)) {
if (this._options.strictSelection) {
return {
content: this._options.errorMessage,

View File

@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as tree from 'vs/base/parts/tree/browser/tree';
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
import { Promise, TPromise } from 'vs/base/common/winjs.base';
import { generateUuid } from 'vs/base/common/uuid';
import * as DOM from 'vs/base/browser/dom';
import { $ } from 'vs/base/browser/builder';
import Event, { Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export interface Template {
label: HTMLElement;
}
export interface Resource {
value: string;
}
export class DropdownModel {
public static ID = generateUuid();
}
export class DropdownRenderer implements tree.IRenderer {
public getHeight(tree: tree.ITree, element: Resource): number {
return 22;
}
public getTemplateId(tree: tree.ITree, element: Resource): string {
return '';
}
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement) {
const row = $('div.list-row').style('height', '22px').style('padding-left', '5px').getHTMLElement();
DOM.append(container, row);
const label = $('span.label').style('margin', 'auto').getHTMLElement();
DOM.append(row, label);
return { label };
}
public renderElement(tree: tree.ITree, element: Resource, templateId: string, templateData: Template): void {
templateData.label.innerText = element.value;
}
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: Template): void {
// no op
}
}
export class DropdownDataSource implements tree.IDataSource {
public options: Array<Resource>;
public getId(tree: tree.ITree, element: Resource | DropdownModel): string {
if (element instanceof DropdownModel) {
return DropdownModel.ID;
} else {
return (element as Resource).value;
}
}
public hasChildren(tree: tree.ITree, element: Resource | DropdownModel): boolean {
if (element instanceof DropdownModel) {
return true;
} else {
return false;
}
}
public getChildren(tree: tree.ITree, element: Resource | DropdownModel): Promise<any, any> {
if (element instanceof DropdownModel) {
return TPromise.as(this.options);
} else {
return TPromise.as(undefined);
}
}
public getParent(tree: tree.ITree, element: Resource | DropdownModel): Promise<any, any> {
if (element instanceof DropdownModel) {
return TPromise.as(undefined);
} else {
return TPromise.as(new DropdownModel());
}
}
}
export class DropdownFilter extends TreeDefaults.DefaultFilter {
public filterString: string;
public isVisible(tree: tree.ITree, element: Resource): boolean {
return element.value.includes(this.filterString);
}
}
export class DropdownController extends TreeDefaults.DefaultController {
private _onSelectionChange = new Emitter<Resource>();
public readonly onSelectionChange: Event<Resource> = this._onSelectionChange.event;
constructor() {
super();
}
protected onLeftClick(tree: tree.ITree, element: any, eventish: TreeDefaults.ICancelableEvent, origin: string): boolean {
let response = super.onLeftClick(tree, element, eventish, origin);
if (response) {
this._onSelectionChange.fire(tree.getSelection()[0]);
}
return response;
}
protected onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean {
let response = super.onEnter(tree, event);
if (response) {
this._onSelectionChange.fire(tree.getSelection()[0]);
}
return response;
}
}

View File

@@ -21,3 +21,7 @@
.dropdown .monaco-action-bar .action-item {
margin: 0;
}
.dropdown-tree .list-row {
margin-left: -33px;
}

View File

@@ -4,16 +4,15 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
import * as lifecycle from 'vs/base/common/lifecycle';
import { SelectBox, ISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox';
import { Color } from 'vs/base/common/color';
import { ISelectBoxStyles } from 'vs/base/browser/ui/selectBox/selectBox';
import { IMessage, MessageType, defaultOpts } from 'vs/base/browser/ui/inputbox/inputBox';
import * as dom from 'vs/base/browser/dom';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IContextViewProvider, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { RenderOptions, renderFormattedText, renderText } from 'vs/base/browser/htmlContentRenderer';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
const $ = dom.$;
@@ -33,14 +32,12 @@ export interface IListBoxStyles {
* Extends SelectBox to allow multiple selection and adding/remove items dynamically
*/
export class ListBox extends SelectBox {
private _toDispose2: lifecycle.IDisposable[];
private enabledSelectBackground: Color;
private enabledSelectForeground: Color;
private enabledSelectBorder: Color;
private disabledSelectBackground: Color;
private disabledSelectForeground: Color;
private disabledSelectBorder: Color;
private keyC = 33;
private inputValidationInfoBorder: Color;
private inputValidationInfoBackground: Color;
@@ -53,7 +50,7 @@ export class ListBox extends SelectBox {
private contextViewProvider: IContextViewProvider;
private isValid: boolean;
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider) {
constructor(options: string[], selectedOption: string, contextViewProvider: IContextViewProvider, private _clipboardService: IClipboardService) {
super(options, 0);
this.contextViewProvider = contextViewProvider;
this.isValid = true;
@@ -64,10 +61,7 @@ export class ListBox extends SelectBox {
this.selectElement.style['width'] = 'inherit';
this.selectElement.style['min-width'] = '100%';
this._toDispose2 = [];
this._toDispose2.push(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => {
this.onKeyDown(e)
}));
this._register(dom.addStandardDisposableListener(this.selectElement, dom.EventType.KEY_DOWN, e => this.onKeyDown(e)));
this.enabledSelectBackground = this.selectBackground;
this.enabledSelectForeground = this.selectForeground;
@@ -88,11 +82,11 @@ export class ListBox extends SelectBox {
}
public style(styles: IListBoxStyles): void {
var superStyle: ISelectBoxStyles = {
let superStyle: ISelectBoxStyles = {
selectBackground: styles.selectBackground,
selectForeground: styles.selectForeground,
selectBorder: styles.selectBorder
}
};
super.style(superStyle);
this.enabledSelectBackground = this.selectBackground;
this.enabledSelectForeground = this.selectForeground;
@@ -123,8 +117,8 @@ export class ListBox extends SelectBox {
}
public get selectedOptions(): string[] {
var selected = [];
for (var i = 0; i < this.selectElement.selectedOptions.length; i++ ) {
let selected = [];
for (let i = 0; i < this.selectElement.selectedOptions.length; i++) {
selected.push(this.selectElement.selectedOptions[i].innerHTML);
}
return selected;
@@ -136,13 +130,13 @@ export class ListBox extends SelectBox {
// Remove selected options
public remove(): void {
var indexes = [];
for (var i = 0; i < this.selectElement.selectedOptions.length; i++ ) {
let indexes = [];
for (let i = 0; i < this.selectElement.selectedOptions.length; i++) {
indexes.push(this.selectElement.selectedOptions[i].index);
}
indexes.sort((a, b) => b-a);
indexes.sort((a, b) => b - a);
for (var i = 0; i < indexes.length; i++) {
for (let i = 0; i < indexes.length; i++) {
this.selectElement.remove(indexes[i]);
this.options.splice(indexes[i], 1);
}
@@ -155,27 +149,22 @@ export class ListBox extends SelectBox {
// Allow copy to clipboard
public onKeyDown(event: IKeyboardEvent): void {
if (this.selectedOptions.length > 0)
{
var key = event.keyCode;
var ctrlOrCmd = event.ctrlKey || event.metaKey;
if (this.selectedOptions.length > 0) {
let key = event.keyCode;
let ctrlOrCmd = event.ctrlKey || event.metaKey;
if (ctrlOrCmd && key === this.keyC) {
var textToCopy = this.selectedOptions[0];
for (var i = 1; i < this.selectedOptions.length; i++) {
textToCopy = textToCopy + ', ' + this.selectedOptions[i];
}
if (ctrlOrCmd && key === KeyCode.KEY_C) {
let textToCopy = this.selectedOptions[0];
for (let i = 1; i < this.selectedOptions.length; i++) {
textToCopy = textToCopy + ', ' + this.selectedOptions[i];
}
// Copy to clipboard
WorkbenchUtils.executeCopy(textToCopy);
event.stopPropagation();
}
}
}
// Copy to clipboard
this._clipboardService.writeText(textToCopy);
public dispose(): void {
this._toDispose2 = lifecycle.dispose(this._toDispose2);
super.dispose();
event.stopPropagation();
}
}
}
public enable(): void {

View File

@@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { Button } from 'sql/base/browser/ui/button/button';
import { Builder } from 'vs/base/browser/builder';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { Button } from 'vs/base/browser/ui/button/button';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import * as data from 'data';
import * as types from 'vs/base/common/types';
import * as data from 'data';
export function appendRow(container: Builder, label: string, labelClass: string, cellContainerClass: string): Builder {
let cellContainer: Builder;
container.element('tr', {}, (rowContainer) => {
@@ -47,35 +47,11 @@ export function appendRowLink(container: Builder, label: string, labelClass: str
return new Builder(rowButton.getElement());
}
export function createCheckBox(container: Builder, label: string, checkboxClass: string, isChecked: boolean, onCheck?: (viaKeyboard: boolean) => void): Checkbox {
let checkbox = new Checkbox({
actionClassName: checkboxClass,
title: label,
isChecked: isChecked,
onChange: (viaKeyboard) => {
if (onCheck) {
onCheck(viaKeyboard);
}
}
});
container.getHTMLElement().appendChild(checkbox.domNode);
container.div({}, (labelContainer) => {
labelContainer.innerHtml(label);
});
return checkbox;
}
export function appendInputSelectBox(container: Builder, selectBox: SelectBox): SelectBox {
selectBox.render(container.getHTMLElement());
return selectBox;
}
export function isNullOrWhiteSpace(value: string): boolean {
// returns true if the string is null or contains white space/tab chars only
return !value || value.trim().length === 0;
}
export function getBooleanValueFromStringOrBoolean(value: any): boolean {
if (types.isBoolean(value)) {
return value;

View File

@@ -154,11 +154,7 @@
width: 100px;
}
.vs-dark.monaco-shell .modal.flyout-dialog .footer-button a.monaco-button.monaco-text-button {
outline-color: #8e8c8c;
}
.modal.flyout-dialog .footer-button a.monaco-button.monaco-text-button:focus {
.vs .monaco-text-button:focus {
outline-width: 1px;
}

View File

@@ -7,17 +7,17 @@ import 'vs/css!./media/modal';
import { IThemable } from 'vs/platform/theme/common/styler';
import { Color } from 'vs/base/common/color';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { KeyCode } from 'vs/base/common/keyCodes';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { mixin } from 'vs/base/common/objects';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Builder, $, withElementById } from 'vs/base/browser/builder';
import { Button } from 'vs/base/browser/ui/button/button';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { generateUuid } from 'vs/base/common/uuid';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { Button } from 'sql/base/browser/ui/button/button';
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
@@ -64,6 +64,12 @@ export abstract class Modal extends Disposable implements IThemable {
private _errorMessage: Builder;
private _spinnerElement: HTMLElement;
private _errorIconElement: HTMLElement;
private _focusableElements: NodeListOf<Element>;
private _firstFocusableElement: HTMLElement;
private _lastFocusableElement: HTMLElement;
private _focusedElementBeforeOpen: HTMLElement;
private _dialogForeground: Color;
private _dialogBorder: Color;
private _dialogHeaderAndFooterBackground: Color;
@@ -102,6 +108,7 @@ export abstract class Modal extends Disposable implements IThemable {
* Set the dialog to have wide layout dynamically.
* Temporary solution to render file browser as wide or narrow layout.
* This will be removed once backup dialog is changed to wide layout.
* (hyoshi - 10/2/2017 tracked by https://github.com/Microsoft/carbon/issues/1836)
*/
public setWide(isWide: boolean): void {
if (this._builder.hasClass('wide') && isWide === false) {
@@ -244,12 +251,42 @@ export abstract class Modal extends Disposable implements IThemable {
this.hide();
}
private handleBackwardTab(e: KeyboardEvent) {
if (this._firstFocusableElement && this._lastFocusableElement && document.activeElement === this._firstFocusableElement) {
e.preventDefault();
this._lastFocusableElement.focus();
}
}
private handleForwardTab(e: KeyboardEvent) {
if (this._firstFocusableElement && this._lastFocusableElement && document.activeElement === this._lastFocusableElement) {
e.preventDefault();
this._firstFocusableElement.focus();
}
}
/**
* Set focusable elements in the modal dialog
*/
public setFocusableElements() {
this._focusableElements = this._builder.getHTMLElement().querySelectorAll('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]');
if (this._focusableElements && this._focusableElements.length > 0) {
this._firstFocusableElement = <HTMLElement>this._focusableElements[0];
this._lastFocusableElement = <HTMLElement>this._focusableElements[this._focusableElements.length - 1];
}
this._focusedElementBeforeOpen = <HTMLElement>document.activeElement;
}
/**
* Shows the modal and attaches key listeners
*/
protected show() {
this._modalShowingContext.get().push(this._staticKey);
this._builder.appendTo(withElementById(this._partService.getWorkbenchElementId()).getHTMLElement().parentElement);
this.setFocusableElements();
this._keydownListener = DOM.addDisposableListener(document, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
let context = this._modalShowingContext.get();
if (context[context.length - 1] === this._staticKey) {
@@ -258,6 +295,10 @@ export abstract class Modal extends Disposable implements IThemable {
this.onAccept(event);
} else if (event.equals(KeyCode.Escape)) {
this.onClose(event);
} else if (event.equals(KeyMod.Shift | KeyCode.Tab)) {
this.handleBackwardTab(e);
} else if (event.equals(KeyCode.Tab)) {
this.handleForwardTab(e);
}
}
});
@@ -281,6 +322,9 @@ export abstract class Modal extends Disposable implements IThemable {
this._footerButtons.forEach(button => button.applyStyles());
this._modalShowingContext.get().pop();
this._builder.offDOM();
if (this._focusedElementBeforeOpen) {
this._focusedElementBeforeOpen.focus();
}
this._keydownListener.dispose();
this._resizeListener.dispose();
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.ModalDialogClosed, { name: this._name });

View File

@@ -6,16 +6,15 @@
'use strict';
import 'vs/css!./media/optionsDialog';
import { Button } from 'sql/base/browser/ui/button/button';
import { FixedCollapsibleView } from 'sql/platform/views/fixedCollapsibleView';
import * as DialogHelper from './dialogHelper';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IModalOptions, Modal } from './modal';
import * as OptionsDialogHelper from './optionsDialogHelper';
import { attachModalDialogStyler } from 'sql/common/theme/styler';
import { attachButtonStyler, attachModalDialogStyler } from 'sql/common/theme/styler';
import * as data from 'data';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import Event, { Emitter } from 'vs/base/common/event';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
@@ -27,13 +26,14 @@ import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/theme
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import * as styler from 'vs/platform/theme/common/styler';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { SplitView, CollapsibleState } from 'vs/base/browser/ui/splitview/splitview';
import { SplitView, CollapsibleState } from 'sql/base/browser/ui/splitview/splitview';
import { Builder, $ } from 'vs/base/browser/builder';
import { Button } from 'vs/base/browser/ui/button/button';
import { Widget } from 'vs/base/browser/ui/widget';
class CategoryView extends FixedCollapsibleView {
export class CategoryView extends FixedCollapsibleView {
private _treecontainer: HTMLElement;
private _collapsed: CollapsibleState;
constructor(private viewTitle: string, private _bodyContainer: HTMLElement, collapsed: boolean, initialBodySize: number, headerSize: number) {
super(
initialBodySize,
@@ -43,6 +43,7 @@ class CategoryView extends FixedCollapsibleView {
initialState: collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED,
ariaHeaderLabel: viewTitle
});
this._collapsed = collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED;
}
public renderHeader(container: HTMLElement): void {
@@ -54,6 +55,7 @@ class CategoryView extends FixedCollapsibleView {
this._treecontainer = document.createElement('div');
container.appendChild(this._treecontainer);
this._treecontainer.appendChild(this._bodyContainer);
this.changeState(this._collapsed);
}
public layoutBody(size: number): void {
@@ -102,13 +104,13 @@ export class OptionsDialog extends Modal {
attachModalDialogStyler(this, this._themeService);
if (this.backButton) {
this.backButton.addListener('click', () => this.cancel());
styler.attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND });
}
this._okButton = this.addFooterButton(this.okLabel, () => this.ok());
this._closeButton = this.addFooterButton(this.cancelLabel, () => this.cancel());
// Theme styler
styler.attachButtonStyler(this._okButton, this._themeService);
styler.attachButtonStyler(this._closeButton, this._themeService);
attachButtonStyler(this._okButton, this._themeService);
attachButtonStyler(this._closeButton, this._themeService);
let self = this;
this._register(self._themeService.onDidColorThemeChange(e => self.updateTheme(e)));
self.updateTheme(self._themeService.getColorTheme());

View File

@@ -6,11 +6,14 @@
import { IThemable } from 'vs/platform/theme/common/styler';
import * as objects from 'vs/base/common/objects';
import Event, { Emitter } from 'vs/base/common/event';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IAction, IActionRunner, Action, IActionChangeEvent, ActionRunner } from 'vs/base/common/actions';
import { Dimension, $, Builder } from 'vs/base/browser/builder';
import { EventType } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import './panelStyles';
import { Disposable } from 'vs/base/common/lifecycle';
export interface IPanelStyles {
@@ -28,45 +31,41 @@ export interface IPanelTab {
}
interface IInternalPanelTab extends IPanelTab {
header: HTMLElement;
label: HTMLElement;
header: Builder;
label: Builder;
}
export type PanelTabIdentifier = string;
export class TabbedPanel implements IThemable {
export class TabbedPanel extends Disposable implements IThemable {
private _tabMap = new Map<PanelTabIdentifier, IInternalPanelTab>();
private _shownTab: PanelTabIdentifier;
public readonly headersize = 35;
private _header: HTMLElement;
private _tabList: HTMLElement;
private $header: Builder;
private $tabList: Builder;
private $body: Builder;
private $parent: Builder;
private _actionbar: ActionBar;
private _body: HTMLElement;
private _currentDimensions: Dimension;
private _collapsed = false;
private _parent: HTMLElement;
private _onTabChange = new Emitter<PanelTabIdentifier>();
public onTabChange: Event<PanelTabIdentifier> = this._onTabChange.event;
constructor(private container: HTMLElement) {
this._parent = document.createElement('div');
this._parent.className = 'tabbedPanel';
container.appendChild(this._parent);
this._header = document.createElement('div');
this._header.className = 'composite title';
this._tabList = document.createElement('div');
this._tabList.className = 'tabList';
this._tabList.style.height = this.headersize + 'px';
this._header.appendChild(this._tabList);
let actionbarcontainer = document.createElement('div');
actionbarcontainer.className = 'title-actions';
super();
this.$parent = this._register($('.tabbedPanel'));
this.$parent.appendTo(container);
this.$header = $('.composite.title');
this.$tabList = $('.tabList');
this.$tabList.style('height', this.headersize + 'px');
this.$header.append(this.$tabList);
let actionbarcontainer = $('.title-actions');
this._actionbar = new ActionBar(actionbarcontainer);
this._header.appendChild(actionbarcontainer);
this._parent.appendChild(this._header);
this._body = document.createElement('div');
this._body.className = 'tabBody';
this._parent.appendChild(this._body);
this.$header.append(actionbarcontainer);
this.$parent.append(this.$header);
this.$body = $('tabBody');
this.$parent.append(this.$body);
}
public pushTab(tab: IPanelTab): PanelTabIdentifier {
@@ -88,14 +87,20 @@ export class TabbedPanel implements IThemable {
}
private _createTab(tab: IInternalPanelTab): void {
let tabElement = document.createElement('div');
tabElement.className = 'tab';
let tabLabel = document.createElement('a');
tabLabel.className = 'tabLabel';
tabLabel.innerText = tab.title;
tabElement.appendChild(tabLabel);
addDisposableListener(tabElement, EventType.CLICK, (e) => this.showTab(tab.identifier));
this._tabList.appendChild(tabElement);
let tabElement = $('.tab');
tabElement.attr('tabindex', '0');
let tabLabel = $('a.tabLabel');
tabLabel.safeInnerHtml(tab.title);
tabElement.append(tabLabel);
tabElement.on(EventType.CLICK, e => this.showTab(tab.identifier));
tabElement.on(EventType.KEY_DOWN, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.showTab(tab.identifier);
e.stopImmediatePropagation();
}
});
this.$tabList.append(tabElement);
tab.header = tabElement;
tab.label = tabLabel;
}
@@ -106,14 +111,14 @@ export class TabbedPanel implements IThemable {
}
if (this._shownTab) {
this._tabMap.get(this._shownTab).label.classList.remove('active');
this._tabMap.get(this._shownTab).label.removeClass('active');
}
this._shownTab = id;
new Builder(this._body).empty();
this.$body.clearChildren();
let tab = this._tabMap.get(this._shownTab);
tab.label.classList.add('active');
tab.view.render(this._body);
tab.label.addClass('active');
tab.view.render(this.$body.getHTMLElement());
this._onTabChange.fire(id);
if (this._currentDimensions) {
this._layoutCurrentTab(new Dimension(this._currentDimensions.width, this._currentDimensions.height - this.headersize));
@@ -121,7 +126,7 @@ export class TabbedPanel implements IThemable {
}
public removeTab(tab: PanelTabIdentifier) {
this._tabMap.get(tab).header.remove();
this._tabMap.get(tab).header.destroy();
this._tabMap.delete(tab);
}
@@ -131,9 +136,9 @@ export class TabbedPanel implements IThemable {
public layout(dimension: Dimension): void {
this._currentDimensions = dimension;
this._header.style.width = dimension.width + 'px';
this._body.style.width = dimension.width + 'px';
this._body.style.height = (dimension.height - this.headersize) + 'px';
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
this.$body.style('height', (dimension.height - this.headersize) + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, dimension.height - this.headersize));
}
@@ -154,9 +159,9 @@ export class TabbedPanel implements IThemable {
this._collapsed = val === false ? false : true;
if (this.collapsed) {
this._body.remove();
this.$body.offDOM();
} else {
this._parent.appendChild(this._body);
this.$parent.append(this.$body);
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e8e8e8" d="M6 4v8l4-4-4-4zm1 2.414l1.586 1.586-1.586 1.586v-3.172z"/></svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#646465" d="M6 4v8l4-4-4-4zm1 2.414l1.586 1.586-1.586 1.586v-3.172z"/></svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e8e8e8" d="M11 10.07h-5.656l5.656-5.656v5.656z"/></svg>

After

Width:  |  Height:  |  Size: 131 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#646465" d="M11 10.07h-5.656l5.656-5.656v5.656z"/></svg>

After

Width:  |  Height:  |  Size: 131 B

View File

@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-split-view {
position: relative;
}
.monaco-split-view > .split-view-view {
overflow: hidden;
}
.monaco-split-view.vertical > .split-view-view {
width: 100%;
}
.monaco-split-view.horizontal > .split-view-view {
height: 100%;
}
.monaco-split-view > .split-view-view > .header {
position: relative;
line-height: 22px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
padding-left: 20px;
overflow: hidden;
display: flex;
}
.monaco-split-view > .split-view-view > .header.hide {
display: none;
}
/* Bold font style does not go well with CJK fonts */
.monaco-split-view:lang(zh-Hans) > .split-view-view > .header,
.monaco-split-view:lang(zh-Hant) > .split-view-view > .header,
.monaco-split-view:lang(ja) > .split-view-view > .header,
.monaco-split-view:lang(ko) > .split-view-view > .header { font-weight: normal; }
.monaco-split-view > .split-view-view > .header.collapsible {
cursor: pointer;
}
.monaco-split-view > .split-view-view > .header.collapsible {
background-image: url('arrow-collapse.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
background-image: url('arrow-expand.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible {
background-image: url('arrow-collapse-dark.svg');
}
.vs-dark .monaco-split-view > .split-view-view > .header.collapsible:not(.collapsed) {
background-image: url('arrow-expand-dark.svg');
background-position: 2px center;
background-repeat: no-repeat;
}
/* Animation */
.monaco-split-view.animated > .split-view-view {
transition-duration: 0.15s;
-webkit-transition-duration: 0.15s;
-moz-transition-duration: 0.15s;
transition-timing-function: ease-out;
-webkit-transition-timing-function: ease-out;
-moz-transition-timing-function: ease-out;
}
.monaco-split-view.vertical.animated > .split-view-view {
transition-property: height;
-webkit-transition-property: height;
-moz-transition-property: height;
}
.monaco-split-view.horizontal.animated > .split-view-view {
transition-property: width;
-webkit-transition-property: width;
-moz-transition-property: width;
}
.hc-black .split-view-view .action-label {
background: none;
}
.hc-black .split-view-view > .header .action-label:before {
top: 4px !important;
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
import 'vs/css!vs/base/browser/ui/checkbox/checkbox';
import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
import { Color } from 'vs/base/common/color';
import * as strings from 'vs/base/common/strings';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
export interface ICheckboxSelectColumnOptions extends Slick.PluginOptions, ICheckboxStyles {
columnId?: string;
@@ -21,24 +21,23 @@ const defaultOptions: ICheckboxSelectColumnOptions = {
columnId: '_checkbox_selector',
cssClass: null,
toolTip: nls.localize('selectDeselectAll', 'Select/Deselect All'),
width: 30,
inputActiveOptionBorder: Color.fromHex('#007ACC')
width: 30
};
const checkBoxTemplate = `<div style="display: flex; align-items: center; flex-direction: column">
<div style="border-color: {0}" role="checkbox" aria-checked="{1}" aria-label="" class="custom-checkbox sql-checkbox {2}"></div>
</div>`;
const checkboxTemplate = `
<div style="display: flex; align-items: center; flex-direction: column">
<input type="checkbox" {0}>
</div>
`;
export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
private _options: ICheckboxSelectColumnOptions;
private _grid: Slick.Grid<T>;
private _handler = new Slick.EventHandler();
private _selectedRowsLookup = {};
private _checkboxTemplate: string;
constructor(options?: Slick.PluginOptions) {
constructor(options?: ICheckboxSelectColumnOptions) {
this._options = mixin(options, defaultOptions, false);
this.applyStyles();
}
public init(grid: Slick.Grid<T>): void {
@@ -74,11 +73,11 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
if (!this._options.title) {
if (selectedRows.length && selectedRows.length === this._grid.getDataLength()) {
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'checked'),
strings.format(checkboxTemplate, 'checked'),
this._options.toolTip);
} else {
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'unchecked'),
strings.format(checkboxTemplate, ''),
this._options.toolTip);
}
}
@@ -94,12 +93,22 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
e.preventDefault();
e.stopImmediatePropagation();
}
} else {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
// clicking on a row select checkbox
if (this._grid.getColumns()[args.cell].id === this._options.columnId) {
this.toggleRowSelection(args.row);
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}
}
private handleClick(e: Event, args: Slick.OnClickEventArgs<T>): void {
// clicking on a row select checkbox
if (this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).is('.custom-checkbox')) {
if (this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).is('input[type="checkbox"]')) {
// if editing, try to commit
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
@@ -122,7 +131,7 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
}
private handleHeaderClick(e: Event, args: Slick.OnHeaderClickEventArgs<T>): void {
if (!this._options.title && args.column.id === this._options.columnId && $(e.target).is('.custom-checkbox')) {
if (!this._options.title && args.column.id === this._options.columnId && $(e.target).is('input[type="checkbox"]')) {
// if editing, try to commit
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
@@ -130,19 +139,19 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
return;
}
if ($(e.target).is('.unchecked')) {
if ($(e.target).is('input[checked]')) {
let rows = [];
for (let i = 0; i < this._grid.getDataLength(); i++) {
rows.push(i);
}
this._grid.setSelectedRows(rows);
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'checked'),
strings.format(checkboxTemplate, 'checked'),
this._options.toolTip);
} else {
this._grid.setSelectedRows([]);
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'unchecked'), this._options.toolTip);
strings.format(checkboxTemplate, ''), this._options.toolTip);
e.stopPropagation();
e.stopImmediatePropagation();
}
@@ -152,7 +161,7 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
public getColumnDefinition(): Slick.Column<T> {
return {
id: this._options.columnId,
name: this._options.title || strings.format(this._checkboxTemplate, 'true', 'unchecked'),
name: this._options.title || strings.format(checkboxTemplate, ''),
toolTip: this._options.toolTip,
field: 'sel',
width: this._options.width,
@@ -166,24 +175,9 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
private checkboxSelectionFormatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return this._selectedRowsLookup[row]
? strings.format(this._checkboxTemplate, 'true', 'checked')
: strings.format(this._checkboxTemplate, 'true', 'unchecked');
? strings.format(checkboxTemplate, 'checked')
: strings.format(checkboxTemplate, '');
}
return null;
}
public style(styles: ICheckboxStyles): void {
if (styles.inputActiveOptionBorder) {
this._options.inputActiveOptionBorder = styles.inputActiveOptionBorder;
}
this.applyStyles();
}
protected applyStyles(): void {
this._checkboxTemplate = strings.format(checkBoxTemplate, this._options.inputActiveOptionBorder.toString(), '{0}', '{1}');
if (this._grid) {
this._grid.invalidateAllRows();
this._grid.render();
}
}
}
}

View File

@@ -82,6 +82,10 @@ export class Table<T extends Slick.SlickData> implements IThemable {
this._grid.setColumns(columns);
}
public get grid(): Slick.Grid<T> {
return this._grid;
}
setData(data: Array<T>);
setData(data: TableDataView<T>);
setData(data: Array<T> | TableDataView<T>) {
@@ -204,11 +208,11 @@ export class Table<T extends Slick.SlickData> implements IThemable {
}
if (styles.listFocusBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { background-color: ${styles.listFocusBackground}; }`);
content.push(`.monaco-table .${this._idPrefix} .slick-row .active { background-color: ${styles.listFocusBackground}; }`);
}
if (styles.listFocusForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { color: ${styles.listFocusForeground}; }`);
content.push(`.monaco-table .${this._idPrefix} .slick-row .active { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {

View File

@@ -5,9 +5,12 @@
import { Table } from './table';
import { TableDataView } from './tableDataView';
import { View, Orientation, AbstractCollapsibleView, HeaderView, IViewOptions, ICollapsibleViewOptions } from 'vs/base/browser/ui/splitview/splitview';
import { View, Orientation, AbstractCollapsibleView, HeaderView, ICollapsibleViewOptions, IViewOptions, CollapsibleState } from 'sql/base/browser/ui/splitview/splitview';
import { $ } from 'vs/base/browser/builder';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import * as DOM from 'vs/base/browser/dom';
import * as lifecycle from 'vs/base/common/lifecycle';
export class TableBasicView<T> extends View {
private _table: Table<T>;
@@ -84,6 +87,7 @@ export class TableHeaderView<T> extends HeaderView {
export class TableCollapsibleView<T> extends AbstractCollapsibleView {
private _table: Table<T>;
private _container: HTMLElement;
private _headerTabListener: lifecycle.IDisposable;
constructor(
private _viewTitle: string,
@@ -98,6 +102,29 @@ export class TableCollapsibleView<T> extends AbstractCollapsibleView {
this._table = new Table<T>(this._container, data, columns, tableOpts);
}
public render(container: HTMLElement, orientation: Orientation): void {
super.render(container, orientation);
this._headerTabListener = DOM.addDisposableListener(this.header, DOM.EventType.KEY_DOWN, (e) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Tab) && this.state === CollapsibleState.EXPANDED) {
let element = this._table.getSelectedRows();
if (!element || element.length === 0) {
this._table.setSelectedRows([0]);
this._table.setActiveCell(0, 1);
e.stopImmediatePropagation();
}
}
});
}
public dispose(): void {
if (this._headerTabListener) {
this._headerTabListener.dispose();
this._headerTabListener = null;
}
super.dispose();
}
public addContainerClass(className: string) {
this._container.classList.add(className);
}

View File

@@ -63,6 +63,15 @@
background-image: url('query-plan-inverse.svg');
}
.vs .icon.actualQueryPlan {
background-image: url('query-plan.svg');
}
.vs-dark .icon.actualQueryPlan,
.hc-black .icon.actualQueryPlan {
background-image: url('query-plan-inverse.svg');
}
.vs .icon.createInsight {
background-image: url('create_insight.svg');
}

View File

@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.custom-view-tree-node-item {
display: flex;
height: 22px;
line-height: 22px;
}
.custom-view-tree-node-item > .custom-view-tree-node-item-icon {
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
padding-right: 6px;
width: 16px;
height: 22px;
-webkit-font-smoothing: antialiased;
}
.custom-view-tree-node-item > .custom-view-tree-node-item-label {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
}

View File

@@ -0,0 +1,303 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { IThemable } from 'vs/platform/theme/common/styler';
import * as errors from 'vs/base/common/errors';
import { $ } from 'vs/base/browser/builder';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { prepareActions } from 'vs/workbench/browser/actions';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { AbstractCollapsibleView, CollapsibleState, IView as IBaseView, SplitView, ViewSizing } from 'sql/base/browser/ui/splitview/splitview';
export interface IViewOptions {
id: string;
name: string;
actionRunner: IActionRunner;
collapsed: boolean;
}
export interface IViewConstructorSignature {
new(initialSize: number, options: IViewOptions, ...services: { _serviceBrand: any; }[]): IView;
}
export interface IView extends IBaseView, IThemable {
id: string;
name: string;
getHeaderElement(): HTMLElement;
create(): TPromise<void>;
setVisible(visible: boolean): TPromise<void>;
isVisible(): boolean;
getActions(): IAction[];
getSecondaryActions(): IAction[];
getActionItem(action: IAction): IActionItem;
getActionsContext(): any;
showHeader(): boolean;
hideHeader(): boolean;
focusBody(): void;
isExpanded(): boolean;
expand(): void;
collapse(): void;
getOptimalWidth(): number;
shutdown(): void;
}
export interface ICollapsibleViewOptions extends IViewOptions {
ariaHeaderLabel?: string;
sizing: ViewSizing;
initialBodySize?: number;
}
export abstract class CollapsibleView extends AbstractCollapsibleView implements IView {
readonly id: string;
readonly name: string;
protected treeContainer: HTMLElement;
protected tree: ITree;
protected toDispose: IDisposable[];
protected toolBar: ToolBar;
protected actionRunner: IActionRunner;
protected isDisposed: boolean;
private _isVisible: boolean;
private dragHandler: DelayedDragHandler;
constructor(
initialSize: number,
options: ICollapsibleViewOptions,
protected keybindingService: IKeybindingService,
protected contextMenuService: IContextMenuService
) {
super(initialSize, {
ariaHeaderLabel: options.ariaHeaderLabel,
sizing: options.sizing,
bodySize: options.initialBodySize ? options.initialBodySize : 4 * 22,
initialState: options.collapsed ? CollapsibleState.COLLAPSED : CollapsibleState.EXPANDED,
});
this.id = options.id;
this.name = options.name;
this.actionRunner = options.actionRunner;
this.toDispose = [];
}
protected changeState(state: CollapsibleState): void {
this.updateTreeVisibility(this.tree, state === CollapsibleState.EXPANDED);
super.changeState(state);
}
get draggableLabel(): string { return this.name; }
public create(): TPromise<void> {
return TPromise.as(null);
}
getHeaderElement(): HTMLElement {
return this.header;
}
public renderHeader(container: HTMLElement): void {
// Tool bar
this.toolBar = new ToolBar($('div.actions').appendTo(container).getHTMLElement(), this.contextMenuService, {
orientation: ActionsOrientation.HORIZONTAL,
actionItemProvider: (action) => this.getActionItem(action),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.name),
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
});
this.toolBar.actionRunner = this.actionRunner;
this.updateActions();
// Expand on drag over
this.dragHandler = new DelayedDragHandler(container, () => {
if (!this.isExpanded()) {
this.expand();
}
});
}
protected updateActions(): void {
this.toolBar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))();
this.toolBar.context = this.getActionsContext();
}
protected renderViewTree(container: HTMLElement): HTMLElement {
const treeContainer = document.createElement('div');
container.appendChild(treeContainer);
return treeContainer;
}
public getViewer(): ITree {
return this.tree;
}
public isVisible(): boolean {
return this._isVisible;
}
public setVisible(visible: boolean): TPromise<void> {
if (this._isVisible !== visible) {
this._isVisible = visible;
this.updateTreeVisibility(this.tree, visible && this.state === CollapsibleState.EXPANDED);
}
return TPromise.as(null);
}
public focusBody(): void {
this.focusTree();
}
protected reveal(element: any, relativeTop?: number): TPromise<void> {
if (!this.tree) {
return TPromise.as(null); // return early if viewlet has not yet been created
}
return this.tree.reveal(element, relativeTop);
}
public layoutBody(size: number): void {
if (this.tree) {
this.treeContainer.style.height = size + 'px';
this.tree.layout(size);
}
}
public getActions(): IAction[] {
return [];
}
public getSecondaryActions(): IAction[] {
return [];
}
public getActionItem(action: IAction): IActionItem {
return null;
}
public getActionsContext(): any {
return undefined;
}
public shutdown(): void {
// Subclass to implement
}
public getOptimalWidth(): number {
return 0;
}
public dispose(): void {
this.isDisposed = true;
this.treeContainer = null;
if (this.tree) {
this.tree.dispose();
}
if (this.dragHandler) {
this.dragHandler.dispose();
}
this.toDispose = dispose(this.toDispose);
if (this.toolBar) {
this.toolBar.dispose();
}
super.dispose();
}
private updateTreeVisibility(tree: ITree, isVisible: boolean): void {
if (!tree) {
return;
}
if (isVisible) {
$(tree.getHTMLElement()).show();
} else {
$(tree.getHTMLElement()).hide(); // make sure the tree goes out of the tabindex world by hiding it
}
if (isVisible) {
tree.onVisible();
} else {
tree.onHidden();
}
}
private focusTree(): void {
if (!this.tree) {
return; // return early if viewlet has not yet been created
}
// Make sure the current selected element is revealed
const selection = this.tree.getSelection();
if (selection.length > 0) {
this.reveal(selection[0], 0.5).done(null, errors.onUnexpectedError);
}
// Pass Focus to Viewer
this.tree.DOMFocus();
}
}
export interface IViewletViewOptions extends IViewOptions {
viewletSettings: object;
}
export interface IViewState {
collapsed: boolean;
size: number | undefined;
isHidden: boolean;
order: number;
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import Event from 'vs/base/common/event';
import { Command } from 'vs/editor/common/modes';
export type TreeViewItemHandleArg = {
$treeViewId: string,
$treeItemHandle: number
};
export enum TreeItemCollapsibleState {
None = 0,
Collapsed = 1,
Expanded = 2
}
export interface ITreeItem {
handle: number;
label: string;
icon?: string;
iconDark?: string;
contextValue?: string;
command?: Command;
children?: ITreeItem[];
collapsibleState?: TreeItemCollapsibleState;
}
export interface ITreeViewDataProvider {
onDidChange: Event<ITreeItem[] | undefined | null>;
onDispose: Event<void>;
getElements(): TPromise<ITreeItem[]>;
getChildren(element: ITreeItem): TPromise<ITreeItem[]>;
}