Files
azuredatastudio/src/sql/base/browser/ui/inputBox/inputBox.ts

207 lines
6.4 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { InputBox as vsInputBox, IInputOptions as vsIInputBoxOptions, IInputBoxStyles as vsIInputBoxStyles, IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Color } from 'vs/base/common/color';
import { Event, Emitter } from 'vs/base/common/event';
import { AdsWidget } from 'sql/base/browser/ui/adsWidget';
export interface OnLoseFocusParams {
value: string;
hasChanged: boolean;
}
export interface IInputBoxStyles extends vsIInputBoxStyles {
disabledInputBackground?: Color;
disabledInputForeground?: Color;
}
export interface IInputOptions extends vsIInputBoxOptions {
/**
* Whether calls to validate require the force parameter to be set to true
* to run the base VS Input Box validation logic. See validate() override
* for more info.
*/
requireForceValidations?: boolean;
required?: boolean;
ariaDescription?: string;
}
export class InputBox extends vsInputBox implements AdsWidget {
private enabledInputBackground?: Color;
private enabledInputForeground?: Color;
private enabledInputBorder?: Color;
private disabledInputBackground?: Color;
private disabledInputForeground?: Color;
private disabledInputBorder?: Color;
private _lastLoseFocusValue: string;
private _onLoseFocus = this._register(new Emitter<OnLoseFocusParams>());
public onLoseFocus: Event<OnLoseFocusParams> = this._onLoseFocus.event;
private _onInputFocus = this._register(new Emitter<void>());
public onInputFocus: Event<void> = this._onInputFocus.event;
private _isTextAreaInput = false;
private _hideErrors = false;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, private _sqlOptions?: IInputOptions, id?: string) {
super(container, contextViewProvider, _sqlOptions);
this.enabledInputBackground = this.inputBackground;
this.enabledInputForeground = this.inputForeground;
this.enabledInputBorder = this.inputBorder;
this.disabledInputBackground = Color.transparent;
this._lastLoseFocusValue = this.value;
let self = this;
this.onblur(this.inputElement, () => {
self._onLoseFocus.fire({ value: self.value, hasChanged: self._lastLoseFocusValue !== self.value });
self._lastLoseFocusValue = self.value;
});
this.onfocus(this.inputElement, () => {
self._onInputFocus.fire();
});
if (_sqlOptions && _sqlOptions.type === 'textarea') {
this._isTextAreaInput = true;
}
this.required = !!this._sqlOptions?.required;
if (this._sqlOptions.ariaDescription) {
this.inputElement.setAttribute('aria-description', this._sqlOptions.ariaDescription);
}
this.inputElement.id = id;
}
public override style(styles: IInputBoxStyles): void {
super.style(styles);
this.enabledInputBackground = this.inputBackground;
this.enabledInputForeground = this.inputForeground;
this.enabledInputBorder = this.inputBorder;
this.disabledInputBackground = styles.disabledInputBackground;
this.disabledInputForeground = styles.disabledInputForeground;
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
public override enable(): void {
super.enable();
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
public set rows(value: number) {
this.inputElement.setAttribute('rows', value.toString());
}
public set columns(value: number) {
this.inputElement.setAttribute('cols', value.toString());
}
public override layout(): void {
if (!this._isTextAreaInput) {
super.layout();
}
}
public override disable(): void {
super.disable();
this.updateInputEnabledDisabledColors();
this.applyStyles();
}
public setHeight(value: string) {
if (this._isTextAreaInput) {
this.inputElement.style.height = value;
this.inputElement.style.whiteSpace = 'normal';
}
}
public setMaxLength(value: number | undefined) {
if (value === undefined) {
this.inputElement.removeAttribute('maxLength');
}
else {
this.inputElement.maxLength = value;
}
}
public set ariaLive(value: string) {
this.element.setAttribute('aria-live', value);
}
public isEnabled(): boolean {
return !this.inputElement.hasAttribute('disabled');
}
public get required(): boolean {
return this.inputElement.required;
}
public set required(v: boolean) {
this.inputElement.required = v;
}
public get hideErrors(): boolean {
return this._hideErrors;
}
public set hideErrors(hideErrors: boolean) {
this._hideErrors = hideErrors;
if (hideErrors) {
this.hideMessage();
}
}
public override showMessage(message: IMessage, force?: boolean): void {
if (!this.hideErrors) {
super.showMessage(message, force);
}
}
private updateInputEnabledDisabledColors(): void {
let enabled = this.isEnabled();
this.inputBackground = enabled ? this.enabledInputBackground : this.disabledInputBackground;
this.inputForeground = enabled ? this.enabledInputForeground : this.disabledInputForeground;
this.inputBorder = enabled ? this.enabledInputBorder : this.disabledInputBorder;
}
public override validate(force?: boolean): MessageType | undefined {
// We override the validate call here because in some situations we could end up with an "invalid" alert
// being announced incorrectly. For example the InputBox component has its own async validation - and so
// if a change was made to the text then the base VS InputBox would call validate immediately - before
// the async validation was able to trigger and complete and so the state could still be invalid at that
// point
// So instead we allow users of the input box to control whether to let the base input box do its validation
// as normal or whether to require manually calling validate with force === true in order to run the validation
// logic.
if (force || this._sqlOptions?.requireForceValidations !== true) {
return super.validate();
}
return undefined;
}
public override set width(width: number) {
super.width = width;
this.element.style.width = 'fit-content';
}
public override get value() {
return super.value;
}
public override set value(newValue: string) {
this._lastLoseFocusValue = newValue;
super.value = newValue;
}
public get id(): string {
return this.input.id;
}
}