Path: blob/main/src/vs/base/browser/ui/findinput/replaceInput.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as dom from '../../dom.js';6import { IKeyboardEvent } from '../../keyboardEvent.js';7import { IMouseEvent } from '../../mouseEvent.js';8import { IToggleStyles, Toggle } from '../toggle/toggle.js';9import { IContextViewProvider } from '../contextview/contextview.js';10import { IFindInputToggleOpts } from './findInputToggles.js';11import { HistoryInputBox, IInputBoxStyles, IInputValidator, IMessage as InputBoxMessage } from '../inputbox/inputBox.js';12import { Widget } from '../widget.js';13import { Codicon } from '../../../common/codicons.js';14import { Emitter, Event } from '../../../common/event.js';15import { KeyCode } from '../../../common/keyCodes.js';16import './findInput.css';17import * as nls from '../../../../nls.js';18import { getDefaultHoverDelegate } from '../hover/hoverDelegateFactory.js';19import { IHistory } from '../../../common/history.js';202122export interface IReplaceInputOptions {23readonly placeholder?: string;24readonly width?: number;25readonly validation?: IInputValidator;26readonly label: string;27readonly flexibleHeight?: boolean;28readonly flexibleWidth?: boolean;29readonly flexibleMaxHeight?: number;3031readonly appendPreserveCaseLabel?: string;32readonly history?: IHistory<string>;33readonly showHistoryHint?: () => boolean;34readonly inputBoxStyles: IInputBoxStyles;35readonly toggleStyles: IToggleStyles;36}3738const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");39const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseToggle', "Preserve Case");4041class PreserveCaseToggle extends Toggle {42constructor(opts: IFindInputToggleOpts) {43super({44// TODO: does this need its own icon?45icon: Codicon.preserveCase,46title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle,47isChecked: opts.isChecked,48hoverDelegate: opts.hoverDelegate ?? getDefaultHoverDelegate('element'),49inputActiveOptionBorder: opts.inputActiveOptionBorder,50inputActiveOptionForeground: opts.inputActiveOptionForeground,51inputActiveOptionBackground: opts.inputActiveOptionBackground,52});53}54}5556export class ReplaceInput extends Widget {5758static readonly OPTION_CHANGE: string = 'optionChange';5960private contextViewProvider: IContextViewProvider | undefined;61private placeholder: string;62private validation?: IInputValidator;63private label: string;64private fixFocusOnOptionClickEnabled = true;6566private preserveCase: PreserveCaseToggle;67private cachedOptionsWidth: number = 0;68public domNode: HTMLElement;69public inputBox: HistoryInputBox;7071private readonly _onDidOptionChange = this._register(new Emitter<boolean>());72public get onDidOptionChange(): Event<boolean /* via keyboard */> { return this._onDidOptionChange.event; }7374private readonly _onKeyDown = this._register(new Emitter<IKeyboardEvent>());75public get onKeyDown(): Event<IKeyboardEvent> { return this._onKeyDown.event; }7677private readonly _onMouseDown = this._register(new Emitter<IMouseEvent>());78public get onMouseDown(): Event<IMouseEvent> { return this._onMouseDown.event; }7980private readonly _onInput = this._register(new Emitter<void>());81public get onInput(): Event<void> { return this._onInput.event; }8283private readonly _onKeyUp = this._register(new Emitter<IKeyboardEvent>());84public get onKeyUp(): Event<IKeyboardEvent> { return this._onKeyUp.event; }8586private _onPreserveCaseKeyDown = this._register(new Emitter<IKeyboardEvent>());87public get onPreserveCaseKeyDown(): Event<IKeyboardEvent> { return this._onPreserveCaseKeyDown.event; }8889constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, private readonly _showOptionButtons: boolean, options: IReplaceInputOptions) {90super();91this.contextViewProvider = contextViewProvider;92this.placeholder = options.placeholder || '';93this.validation = options.validation;94this.label = options.label || NLS_DEFAULT_LABEL;9596const appendPreserveCaseLabel = options.appendPreserveCaseLabel || '';97const history = options.history || new Set([]);98const flexibleHeight = !!options.flexibleHeight;99const flexibleWidth = !!options.flexibleWidth;100const flexibleMaxHeight = options.flexibleMaxHeight;101102this.domNode = document.createElement('div');103this.domNode.classList.add('monaco-findInput');104105this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, {106ariaLabel: this.label || '',107placeholder: this.placeholder || '',108validationOptions: {109validation: this.validation110},111history,112showHistoryHint: options.showHistoryHint,113flexibleHeight,114flexibleWidth,115flexibleMaxHeight,116inputBoxStyles: options.inputBoxStyles117}));118119this.preserveCase = this._register(new PreserveCaseToggle({120appendTitle: appendPreserveCaseLabel,121isChecked: false,122...options.toggleStyles123}));124this._register(this.preserveCase.onChange(viaKeyboard => {125this._onDidOptionChange.fire(viaKeyboard);126if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {127this.inputBox.focus();128}129this.validate();130}));131this._register(this.preserveCase.onKeyDown(e => {132this._onPreserveCaseKeyDown.fire(e);133}));134135if (this._showOptionButtons) {136this.cachedOptionsWidth = this.preserveCase.width();137} else {138this.cachedOptionsWidth = 0;139}140141// Arrow-Key support to navigate between options142const indexes = [this.preserveCase.domNode];143this.onkeydown(this.domNode, (event: IKeyboardEvent) => {144if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {145const index = indexes.indexOf(<HTMLElement>this.domNode.ownerDocument.activeElement);146if (index >= 0) {147let newIndex: number = -1;148if (event.equals(KeyCode.RightArrow)) {149newIndex = (index + 1) % indexes.length;150} else if (event.equals(KeyCode.LeftArrow)) {151if (index === 0) {152newIndex = indexes.length - 1;153} else {154newIndex = index - 1;155}156}157158if (event.equals(KeyCode.Escape)) {159indexes[index].blur();160this.inputBox.focus();161} else if (newIndex >= 0) {162indexes[newIndex].focus();163}164165dom.EventHelper.stop(event, true);166}167}168});169170171const controls = document.createElement('div');172controls.className = 'controls';173controls.style.display = this._showOptionButtons ? 'block' : 'none';174controls.appendChild(this.preserveCase.domNode);175176this.domNode.appendChild(controls);177178parent?.appendChild(this.domNode);179180this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));181this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));182this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());183this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));184}185186public enable(): void {187this.domNode.classList.remove('disabled');188this.inputBox.enable();189this.preserveCase.enable();190}191192public disable(): void {193this.domNode.classList.add('disabled');194this.inputBox.disable();195this.preserveCase.disable();196}197198public setFocusInputOnOptionClick(value: boolean): void {199this.fixFocusOnOptionClickEnabled = value;200}201202public setEnabled(enabled: boolean): void {203if (enabled) {204this.enable();205} else {206this.disable();207}208}209210public clear(): void {211this.clearValidation();212this.setValue('');213this.focus();214}215216public getValue(): string {217return this.inputBox.value;218}219220public setValue(value: string): void {221if (this.inputBox.value !== value) {222this.inputBox.value = value;223}224}225226public onSearchSubmit(): void {227this.inputBox.addToHistory();228}229230protected applyStyles(): void {231}232233public select(): void {234this.inputBox.select();235}236237public focus(): void {238this.inputBox.focus();239}240241public getPreserveCase(): boolean {242return this.preserveCase.checked;243}244245public setPreserveCase(value: boolean): void {246this.preserveCase.checked = value;247}248249public focusOnPreserve(): void {250this.preserveCase.focus();251}252253private _lastHighlightFindOptions: number = 0;254public highlightFindOptions(): void {255this.domNode.classList.remove('highlight-' + (this._lastHighlightFindOptions));256this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;257this.domNode.classList.add('highlight-' + (this._lastHighlightFindOptions));258}259260public validate(): void {261this.inputBox?.validate();262}263264public showMessage(message: InputBoxMessage): void {265this.inputBox?.showMessage(message);266}267268public clearMessage(): void {269this.inputBox?.hideMessage();270}271272private clearValidation(): void {273this.inputBox?.hideMessage();274}275276public set width(newWidth: number) {277this.inputBox.paddingRight = this.cachedOptionsWidth;278this.domNode.style.width = newWidth + 'px';279}280281public override dispose(): void {282super.dispose();283}284}285286287