Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/ui/selectBox/selectBoxNative.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 { EventType, Gesture } from '../../touch.js';
8
import { ISelectBoxDelegate, ISelectBoxOptions, ISelectBoxStyles, ISelectData, ISelectOptionItem } from './selectBox.js';
9
import * as arrays from '../../../common/arrays.js';
10
import { Emitter, Event } from '../../../common/event.js';
11
import { KeyCode } from '../../../common/keyCodes.js';
12
import { Disposable } from '../../../common/lifecycle.js';
13
import { isMacintosh } from '../../../common/platform.js';
14
15
export class SelectBoxNative extends Disposable implements ISelectBoxDelegate {
16
17
private selectElement: HTMLSelectElement;
18
private selectBoxOptions: ISelectBoxOptions;
19
private options: ISelectOptionItem[];
20
private selected = 0;
21
private readonly _onDidSelect: Emitter<ISelectData>;
22
private styles: ISelectBoxStyles;
23
24
constructor(options: ISelectOptionItem[], selected: number, styles: ISelectBoxStyles, selectBoxOptions?: ISelectBoxOptions) {
25
super();
26
this.selectBoxOptions = selectBoxOptions || Object.create(null);
27
28
this.options = [];
29
30
this.selectElement = document.createElement('select');
31
32
this.selectElement.className = 'monaco-select-box';
33
34
if (typeof this.selectBoxOptions.ariaLabel === 'string') {
35
this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel);
36
}
37
38
if (typeof this.selectBoxOptions.ariaDescription === 'string') {
39
this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription);
40
}
41
42
this._onDidSelect = this._register(new Emitter<ISelectData>());
43
44
this.styles = styles;
45
46
this.registerListeners();
47
this.setOptions(options, selected);
48
}
49
50
private registerListeners() {
51
this._register(Gesture.addTarget(this.selectElement));
52
[EventType.Tap].forEach(eventType => {
53
this._register(dom.addDisposableListener(this.selectElement, eventType, (e) => {
54
this.selectElement.focus();
55
}));
56
});
57
58
this._register(dom.addStandardDisposableListener(this.selectElement, 'click', (e) => {
59
dom.EventHelper.stop(e, true);
60
}));
61
62
this._register(dom.addStandardDisposableListener(this.selectElement, 'change', (e) => {
63
this.selectElement.title = e.target.value;
64
this._onDidSelect.fire({
65
index: e.target.selectedIndex,
66
selected: e.target.value
67
});
68
}));
69
70
this._register(dom.addStandardDisposableListener(this.selectElement, 'keydown', (e) => {
71
let showSelect = false;
72
73
if (isMacintosh) {
74
if (e.keyCode === KeyCode.DownArrow || e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.Space) {
75
showSelect = true;
76
}
77
} else {
78
if (e.keyCode === KeyCode.DownArrow && e.altKey || e.keyCode === KeyCode.Space || e.keyCode === KeyCode.Enter) {
79
showSelect = true;
80
}
81
}
82
83
if (showSelect) {
84
// Space, Enter, is used to expand select box, do not propagate it (prevent action bar action run)
85
e.stopPropagation();
86
}
87
}));
88
}
89
90
public get onDidSelect(): Event<ISelectData> {
91
return this._onDidSelect.event;
92
}
93
94
public setOptions(options: ISelectOptionItem[], selected?: number): void {
95
96
if (!this.options || !arrays.equals(this.options, options)) {
97
this.options = options;
98
this.selectElement.options.length = 0;
99
100
this.options.forEach((option, index) => {
101
this.selectElement.add(this.createOption(option.text, index, option.isDisabled));
102
});
103
104
}
105
106
if (selected !== undefined) {
107
this.select(selected);
108
}
109
}
110
111
public select(index: number): void {
112
if (this.options.length === 0) {
113
this.selected = 0;
114
} else if (index >= 0 && index < this.options.length) {
115
this.selected = index;
116
} else if (index > this.options.length - 1) {
117
// Adjust index to end of list
118
// This could make client out of sync with the select
119
this.select(this.options.length - 1);
120
} else if (this.selected < 0) {
121
this.selected = 0;
122
}
123
124
this.selectElement.selectedIndex = this.selected;
125
if ((this.selected < this.options.length) && typeof this.options[this.selected].text === 'string') {
126
this.selectElement.title = this.options[this.selected].text;
127
} else {
128
this.selectElement.title = '';
129
}
130
}
131
132
public setAriaLabel(label: string): void {
133
this.selectBoxOptions.ariaLabel = label;
134
this.selectElement.setAttribute('aria-label', label);
135
}
136
137
public focus(): void {
138
if (this.selectElement) {
139
this.selectElement.tabIndex = 0;
140
this.selectElement.focus();
141
}
142
}
143
144
public blur(): void {
145
if (this.selectElement) {
146
this.selectElement.tabIndex = -1;
147
this.selectElement.blur();
148
}
149
}
150
151
public setEnabled(enable: boolean): void {
152
this.selectElement.disabled = !enable;
153
}
154
155
public setFocusable(focusable: boolean): void {
156
this.selectElement.tabIndex = focusable ? 0 : -1;
157
}
158
159
public render(container: HTMLElement): void {
160
container.classList.add('select-container');
161
container.appendChild(this.selectElement);
162
this.setOptions(this.options, this.selected);
163
this.applyStyles();
164
}
165
166
public style(styles: ISelectBoxStyles): void {
167
this.styles = styles;
168
this.applyStyles();
169
}
170
171
public applyStyles(): void {
172
173
// Style native select
174
if (this.selectElement) {
175
this.selectElement.style.backgroundColor = this.styles.selectBackground ?? '';
176
this.selectElement.style.color = this.styles.selectForeground ?? '';
177
this.selectElement.style.borderColor = this.styles.selectBorder ?? '';
178
}
179
180
}
181
182
private createOption(value: string, index: number, disabled?: boolean): HTMLOptionElement {
183
const option = document.createElement('option');
184
option.value = value;
185
option.text = value;
186
option.disabled = !!disabled;
187
188
return option;
189
}
190
}
191
192