Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/ui/findinput/replaceInput.ts
5236 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../dom.js';
7
import { IKeyboardEvent } from '../../keyboardEvent.js';
8
import { IMouseEvent } from '../../mouseEvent.js';
9
import { IToggleStyles, Toggle } from '../toggle/toggle.js';
10
import { IContextViewProvider } from '../contextview/contextview.js';
11
import { IFindInputToggleOpts } from './findInputToggles.js';
12
import { HistoryInputBox, IInputBoxStyles, IInputValidator, IMessage as InputBoxMessage } from '../inputbox/inputBox.js';
13
import { Widget } from '../widget.js';
14
import { Codicon } from '../../../common/codicons.js';
15
import { Emitter, Event } from '../../../common/event.js';
16
import { KeyCode } from '../../../common/keyCodes.js';
17
import './findInput.css';
18
import * as nls from '../../../../nls.js';
19
import { IHistory } from '../../../common/history.js';
20
import { type IHoverLifecycleOptions } from '../hover/hover.js';
21
22
23
export interface IReplaceInputOptions {
24
readonly placeholder?: string;
25
readonly width?: number;
26
readonly validation?: IInputValidator;
27
readonly label: string;
28
readonly flexibleHeight?: boolean;
29
readonly flexibleWidth?: boolean;
30
readonly flexibleMaxHeight?: number;
31
readonly hoverLifecycleOptions?: IHoverLifecycleOptions;
32
33
readonly appendPreserveCaseLabel?: string;
34
readonly history?: IHistory<string>;
35
readonly showHistoryHint?: () => boolean;
36
readonly inputBoxStyles: IInputBoxStyles;
37
readonly toggleStyles: IToggleStyles;
38
}
39
40
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
41
const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseToggle', "Preserve Case");
42
43
class PreserveCaseToggle extends Toggle {
44
constructor(opts: IFindInputToggleOpts) {
45
super({
46
// TODO: does this need its own icon?
47
icon: Codicon.preserveCase,
48
title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle,
49
isChecked: opts.isChecked,
50
hoverLifecycleOptions: opts.hoverLifecycleOptions,
51
inputActiveOptionBorder: opts.inputActiveOptionBorder,
52
inputActiveOptionForeground: opts.inputActiveOptionForeground,
53
inputActiveOptionBackground: opts.inputActiveOptionBackground,
54
});
55
}
56
}
57
58
export class ReplaceInput extends Widget {
59
60
static readonly OPTION_CHANGE: string = 'optionChange';
61
62
private contextViewProvider: IContextViewProvider | undefined;
63
private placeholder: string;
64
private validation?: IInputValidator;
65
private label: string;
66
private fixFocusOnOptionClickEnabled = true;
67
68
private preserveCase: PreserveCaseToggle;
69
private cachedOptionsWidth: number = 0;
70
public domNode: HTMLElement;
71
public inputBox: HistoryInputBox;
72
73
private readonly _onDidOptionChange = this._register(new Emitter<boolean>());
74
public get onDidOptionChange(): Event<boolean /* via keyboard */> { return this._onDidOptionChange.event; }
75
76
private readonly _onKeyDown = this._register(new Emitter<IKeyboardEvent>());
77
public get onKeyDown(): Event<IKeyboardEvent> { return this._onKeyDown.event; }
78
79
private readonly _onMouseDown = this._register(new Emitter<IMouseEvent>());
80
public get onMouseDown(): Event<IMouseEvent> { return this._onMouseDown.event; }
81
82
private readonly _onInput = this._register(new Emitter<void>());
83
public get onInput(): Event<void> { return this._onInput.event; }
84
85
private readonly _onKeyUp = this._register(new Emitter<IKeyboardEvent>());
86
public get onKeyUp(): Event<IKeyboardEvent> { return this._onKeyUp.event; }
87
88
private _onPreserveCaseKeyDown = this._register(new Emitter<IKeyboardEvent>());
89
public get onPreserveCaseKeyDown(): Event<IKeyboardEvent> { return this._onPreserveCaseKeyDown.event; }
90
91
constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, private readonly _showOptionButtons: boolean, options: IReplaceInputOptions) {
92
super();
93
this.contextViewProvider = contextViewProvider;
94
this.placeholder = options.placeholder || '';
95
this.validation = options.validation;
96
this.label = options.label || NLS_DEFAULT_LABEL;
97
98
const appendPreserveCaseLabel = options.appendPreserveCaseLabel || '';
99
const history = options.history || new Set([]);
100
const flexibleHeight = !!options.flexibleHeight;
101
const flexibleWidth = !!options.flexibleWidth;
102
const flexibleMaxHeight = options.flexibleMaxHeight;
103
104
this.domNode = document.createElement('div');
105
this.domNode.classList.add('monaco-findInput');
106
107
this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, {
108
ariaLabel: this.label || '',
109
placeholder: this.placeholder || '',
110
validationOptions: {
111
validation: this.validation
112
},
113
history,
114
showHistoryHint: options.showHistoryHint,
115
flexibleHeight,
116
flexibleWidth,
117
flexibleMaxHeight,
118
inputBoxStyles: options.inputBoxStyles
119
}));
120
121
this.preserveCase = this._register(new PreserveCaseToggle({
122
appendTitle: appendPreserveCaseLabel,
123
isChecked: false,
124
hoverLifecycleOptions: options.hoverLifecycleOptions,
125
...options.toggleStyles
126
}));
127
this._register(this.preserveCase.onChange(viaKeyboard => {
128
this._onDidOptionChange.fire(viaKeyboard);
129
if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {
130
this.inputBox.focus();
131
}
132
this.validate();
133
}));
134
this._register(this.preserveCase.onKeyDown(e => {
135
this._onPreserveCaseKeyDown.fire(e);
136
}));
137
138
if (this._showOptionButtons) {
139
this.cachedOptionsWidth = this.preserveCase.width();
140
} else {
141
this.cachedOptionsWidth = 0;
142
}
143
144
// Arrow-Key support to navigate between options
145
const indexes = [this.preserveCase.domNode];
146
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
147
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
148
const index = indexes.indexOf(<HTMLElement>this.domNode.ownerDocument.activeElement);
149
if (index >= 0) {
150
let newIndex: number = -1;
151
if (event.equals(KeyCode.RightArrow)) {
152
newIndex = (index + 1) % indexes.length;
153
} else if (event.equals(KeyCode.LeftArrow)) {
154
if (index === 0) {
155
newIndex = indexes.length - 1;
156
} else {
157
newIndex = index - 1;
158
}
159
}
160
161
if (event.equals(KeyCode.Escape)) {
162
indexes[index].blur();
163
this.inputBox.focus();
164
} else if (newIndex >= 0) {
165
indexes[newIndex].focus();
166
}
167
168
dom.EventHelper.stop(event, true);
169
}
170
}
171
});
172
173
174
const controls = document.createElement('div');
175
controls.className = 'controls';
176
controls.style.display = this._showOptionButtons ? 'block' : 'none';
177
controls.appendChild(this.preserveCase.domNode);
178
179
this.domNode.appendChild(controls);
180
181
parent?.appendChild(this.domNode);
182
183
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
184
this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
185
this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());
186
this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));
187
}
188
189
public enable(): void {
190
this.domNode.classList.remove('disabled');
191
this.inputBox.enable();
192
this.preserveCase.enable();
193
}
194
195
public disable(): void {
196
this.domNode.classList.add('disabled');
197
this.inputBox.disable();
198
this.preserveCase.disable();
199
}
200
201
public setFocusInputOnOptionClick(value: boolean): void {
202
this.fixFocusOnOptionClickEnabled = value;
203
}
204
205
public setEnabled(enabled: boolean): void {
206
if (enabled) {
207
this.enable();
208
} else {
209
this.disable();
210
}
211
}
212
213
public clear(): void {
214
this.clearValidation();
215
this.setValue('');
216
this.focus();
217
}
218
219
public getValue(): string {
220
return this.inputBox.value;
221
}
222
223
public setValue(value: string): void {
224
if (this.inputBox.value !== value) {
225
this.inputBox.value = value;
226
}
227
}
228
229
public onSearchSubmit(): void {
230
this.inputBox.addToHistory();
231
}
232
233
protected applyStyles(): void {
234
}
235
236
public select(): void {
237
this.inputBox.select();
238
}
239
240
public focus(): void {
241
this.inputBox.focus();
242
}
243
244
public getPreserveCase(): boolean {
245
return this.preserveCase.checked;
246
}
247
248
public setPreserveCase(value: boolean): void {
249
this.preserveCase.checked = value;
250
}
251
252
public focusOnPreserve(): void {
253
this.preserveCase.focus();
254
}
255
256
private _lastHighlightFindOptions: number = 0;
257
public highlightFindOptions(): void {
258
this.domNode.classList.remove('highlight-' + (this._lastHighlightFindOptions));
259
this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;
260
this.domNode.classList.add('highlight-' + (this._lastHighlightFindOptions));
261
}
262
263
public validate(): void {
264
this.inputBox?.validate();
265
}
266
267
public showMessage(message: InputBoxMessage): void {
268
this.inputBox?.showMessage(message);
269
}
270
271
public clearMessage(): void {
272
this.inputBox?.hideMessage();
273
}
274
275
private clearValidation(): void {
276
this.inputBox?.hideMessage();
277
}
278
279
public set width(newWidth: number) {
280
this.inputBox.paddingRight = this.cachedOptionsWidth;
281
this.domNode.style.width = newWidth + 'px';
282
}
283
284
public override dispose(): void {
285
super.dispose();
286
}
287
}
288
289