Path: blob/main/src/vs/base/browser/ui/findinput/replaceInput.ts
5236 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 { IHistory } from '../../../common/history.js';19import { type IHoverLifecycleOptions } from '../hover/hover.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;30readonly hoverLifecycleOptions?: IHoverLifecycleOptions;3132readonly appendPreserveCaseLabel?: string;33readonly history?: IHistory<string>;34readonly showHistoryHint?: () => boolean;35readonly inputBoxStyles: IInputBoxStyles;36readonly toggleStyles: IToggleStyles;37}3839const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");40const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseToggle', "Preserve Case");4142class PreserveCaseToggle extends Toggle {43constructor(opts: IFindInputToggleOpts) {44super({45// TODO: does this need its own icon?46icon: Codicon.preserveCase,47title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle,48isChecked: opts.isChecked,49hoverLifecycleOptions: opts.hoverLifecycleOptions,50inputActiveOptionBorder: opts.inputActiveOptionBorder,51inputActiveOptionForeground: opts.inputActiveOptionForeground,52inputActiveOptionBackground: opts.inputActiveOptionBackground,53});54}55}5657export class ReplaceInput extends Widget {5859static readonly OPTION_CHANGE: string = 'optionChange';6061private contextViewProvider: IContextViewProvider | undefined;62private placeholder: string;63private validation?: IInputValidator;64private label: string;65private fixFocusOnOptionClickEnabled = true;6667private preserveCase: PreserveCaseToggle;68private cachedOptionsWidth: number = 0;69public domNode: HTMLElement;70public inputBox: HistoryInputBox;7172private readonly _onDidOptionChange = this._register(new Emitter<boolean>());73public get onDidOptionChange(): Event<boolean /* via keyboard */> { return this._onDidOptionChange.event; }7475private readonly _onKeyDown = this._register(new Emitter<IKeyboardEvent>());76public get onKeyDown(): Event<IKeyboardEvent> { return this._onKeyDown.event; }7778private readonly _onMouseDown = this._register(new Emitter<IMouseEvent>());79public get onMouseDown(): Event<IMouseEvent> { return this._onMouseDown.event; }8081private readonly _onInput = this._register(new Emitter<void>());82public get onInput(): Event<void> { return this._onInput.event; }8384private readonly _onKeyUp = this._register(new Emitter<IKeyboardEvent>());85public get onKeyUp(): Event<IKeyboardEvent> { return this._onKeyUp.event; }8687private _onPreserveCaseKeyDown = this._register(new Emitter<IKeyboardEvent>());88public get onPreserveCaseKeyDown(): Event<IKeyboardEvent> { return this._onPreserveCaseKeyDown.event; }8990constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, private readonly _showOptionButtons: boolean, options: IReplaceInputOptions) {91super();92this.contextViewProvider = contextViewProvider;93this.placeholder = options.placeholder || '';94this.validation = options.validation;95this.label = options.label || NLS_DEFAULT_LABEL;9697const appendPreserveCaseLabel = options.appendPreserveCaseLabel || '';98const history = options.history || new Set([]);99const flexibleHeight = !!options.flexibleHeight;100const flexibleWidth = !!options.flexibleWidth;101const flexibleMaxHeight = options.flexibleMaxHeight;102103this.domNode = document.createElement('div');104this.domNode.classList.add('monaco-findInput');105106this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, {107ariaLabel: this.label || '',108placeholder: this.placeholder || '',109validationOptions: {110validation: this.validation111},112history,113showHistoryHint: options.showHistoryHint,114flexibleHeight,115flexibleWidth,116flexibleMaxHeight,117inputBoxStyles: options.inputBoxStyles118}));119120this.preserveCase = this._register(new PreserveCaseToggle({121appendTitle: appendPreserveCaseLabel,122isChecked: false,123hoverLifecycleOptions: options.hoverLifecycleOptions,124...options.toggleStyles125}));126this._register(this.preserveCase.onChange(viaKeyboard => {127this._onDidOptionChange.fire(viaKeyboard);128if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {129this.inputBox.focus();130}131this.validate();132}));133this._register(this.preserveCase.onKeyDown(e => {134this._onPreserveCaseKeyDown.fire(e);135}));136137if (this._showOptionButtons) {138this.cachedOptionsWidth = this.preserveCase.width();139} else {140this.cachedOptionsWidth = 0;141}142143// Arrow-Key support to navigate between options144const indexes = [this.preserveCase.domNode];145this.onkeydown(this.domNode, (event: IKeyboardEvent) => {146if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {147const index = indexes.indexOf(<HTMLElement>this.domNode.ownerDocument.activeElement);148if (index >= 0) {149let newIndex: number = -1;150if (event.equals(KeyCode.RightArrow)) {151newIndex = (index + 1) % indexes.length;152} else if (event.equals(KeyCode.LeftArrow)) {153if (index === 0) {154newIndex = indexes.length - 1;155} else {156newIndex = index - 1;157}158}159160if (event.equals(KeyCode.Escape)) {161indexes[index].blur();162this.inputBox.focus();163} else if (newIndex >= 0) {164indexes[newIndex].focus();165}166167dom.EventHelper.stop(event, true);168}169}170});171172173const controls = document.createElement('div');174controls.className = 'controls';175controls.style.display = this._showOptionButtons ? 'block' : 'none';176controls.appendChild(this.preserveCase.domNode);177178this.domNode.appendChild(controls);179180parent?.appendChild(this.domNode);181182this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));183this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));184this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());185this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));186}187188public enable(): void {189this.domNode.classList.remove('disabled');190this.inputBox.enable();191this.preserveCase.enable();192}193194public disable(): void {195this.domNode.classList.add('disabled');196this.inputBox.disable();197this.preserveCase.disable();198}199200public setFocusInputOnOptionClick(value: boolean): void {201this.fixFocusOnOptionClickEnabled = value;202}203204public setEnabled(enabled: boolean): void {205if (enabled) {206this.enable();207} else {208this.disable();209}210}211212public clear(): void {213this.clearValidation();214this.setValue('');215this.focus();216}217218public getValue(): string {219return this.inputBox.value;220}221222public setValue(value: string): void {223if (this.inputBox.value !== value) {224this.inputBox.value = value;225}226}227228public onSearchSubmit(): void {229this.inputBox.addToHistory();230}231232protected applyStyles(): void {233}234235public select(): void {236this.inputBox.select();237}238239public focus(): void {240this.inputBox.focus();241}242243public getPreserveCase(): boolean {244return this.preserveCase.checked;245}246247public setPreserveCase(value: boolean): void {248this.preserveCase.checked = value;249}250251public focusOnPreserve(): void {252this.preserveCase.focus();253}254255private _lastHighlightFindOptions: number = 0;256public highlightFindOptions(): void {257this.domNode.classList.remove('highlight-' + (this._lastHighlightFindOptions));258this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;259this.domNode.classList.add('highlight-' + (this._lastHighlightFindOptions));260}261262public validate(): void {263this.inputBox?.validate();264}265266public showMessage(message: InputBoxMessage): void {267this.inputBox?.showMessage(message);268}269270public clearMessage(): void {271this.inputBox?.hideMessage();272}273274private clearValidation(): void {275this.inputBox?.hideMessage();276}277278public set width(newWidth: number) {279this.inputBox.paddingRight = this.cachedOptionsWidth;280this.domNode.style.width = newWidth + 'px';281}282283public override dispose(): void {284super.dispose();285}286}287288289