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