Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/browser/ui/progressbar/progressbar.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 { hide, show } from '../../dom.js';
7
import { getProgressAcccessibilitySignalScheduler } from './progressAccessibilitySignal.js';
8
import { RunOnceScheduler } from '../../../common/async.js';
9
import { Disposable, IDisposable, MutableDisposable } from '../../../common/lifecycle.js';
10
import { isNumber } from '../../../common/types.js';
11
import './progressbar.css';
12
13
const CSS_DONE = 'done';
14
const CSS_ACTIVE = 'active';
15
const CSS_INFINITE = 'infinite';
16
const CSS_INFINITE_LONG_RUNNING = 'infinite-long-running';
17
const CSS_DISCRETE = 'discrete';
18
19
export interface IProgressBarOptions extends IProgressBarStyles {
20
}
21
22
export interface IProgressBarStyles {
23
progressBarBackground: string | undefined;
24
}
25
26
export const unthemedProgressBarOptions: IProgressBarOptions = {
27
progressBarBackground: undefined
28
};
29
30
/**
31
* A progress bar with support for infinite or discrete progress.
32
*/
33
export class ProgressBar extends Disposable {
34
35
/**
36
* After a certain time of showing the progress bar, switch
37
* to long-running mode and throttle animations to reduce
38
* the pressure on the GPU process.
39
*
40
* https://github.com/microsoft/vscode/issues/97900
41
* https://github.com/microsoft/vscode/issues/138396
42
*/
43
private static readonly LONG_RUNNING_INFINITE_THRESHOLD = 10000;
44
45
private static readonly PROGRESS_SIGNAL_DEFAULT_DELAY = 3000;
46
47
private workedVal: number;
48
private element!: HTMLElement;
49
private bit!: HTMLElement;
50
private totalWork: number | undefined;
51
private showDelayedScheduler: RunOnceScheduler;
52
private longRunningScheduler: RunOnceScheduler;
53
private readonly progressSignal = this._register(new MutableDisposable<IDisposable>());
54
55
constructor(container: HTMLElement, options?: IProgressBarOptions) {
56
super();
57
58
this.workedVal = 0;
59
60
this.showDelayedScheduler = this._register(new RunOnceScheduler(() => show(this.element), 0));
61
this.longRunningScheduler = this._register(new RunOnceScheduler(() => this.infiniteLongRunning(), ProgressBar.LONG_RUNNING_INFINITE_THRESHOLD));
62
63
this.create(container, options);
64
}
65
66
private create(container: HTMLElement, options?: IProgressBarOptions): void {
67
this.element = document.createElement('div');
68
this.element.classList.add('monaco-progress-container');
69
this.element.setAttribute('role', 'progressbar');
70
this.element.setAttribute('aria-valuemin', '0');
71
container.appendChild(this.element);
72
73
this.bit = document.createElement('div');
74
this.bit.classList.add('progress-bit');
75
this.bit.style.backgroundColor = options?.progressBarBackground || '#0E70C0';
76
this.element.appendChild(this.bit);
77
}
78
79
private off(): void {
80
this.bit.style.width = 'inherit';
81
this.bit.style.opacity = '1';
82
this.element.classList.remove(CSS_ACTIVE, CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DISCRETE);
83
84
this.workedVal = 0;
85
this.totalWork = undefined;
86
87
this.longRunningScheduler.cancel();
88
this.progressSignal.clear();
89
}
90
91
/**
92
* Indicates to the progress bar that all work is done.
93
*/
94
done(): ProgressBar {
95
return this.doDone(true);
96
}
97
98
/**
99
* Stops the progressbar from showing any progress instantly without fading out.
100
*/
101
stop(): ProgressBar {
102
return this.doDone(false);
103
}
104
105
private doDone(delayed: boolean): ProgressBar {
106
this.element.classList.add(CSS_DONE);
107
108
// discrete: let it grow to 100% width and hide afterwards
109
if (!this.element.classList.contains(CSS_INFINITE)) {
110
this.bit.style.width = 'inherit';
111
112
if (delayed) {
113
setTimeout(() => this.off(), 200);
114
} else {
115
this.off();
116
}
117
}
118
119
// infinite: let it fade out and hide afterwards
120
else {
121
this.bit.style.opacity = '0';
122
if (delayed) {
123
setTimeout(() => this.off(), 200);
124
} else {
125
this.off();
126
}
127
}
128
129
return this;
130
}
131
132
/**
133
* Use this mode to indicate progress that has no total number of work units.
134
*/
135
infinite(): ProgressBar {
136
this.bit.style.width = '2%';
137
this.bit.style.opacity = '1';
138
139
this.element.classList.remove(CSS_DISCRETE, CSS_DONE, CSS_INFINITE_LONG_RUNNING);
140
this.element.classList.add(CSS_ACTIVE, CSS_INFINITE);
141
142
this.longRunningScheduler.schedule();
143
144
return this;
145
}
146
147
private infiniteLongRunning(): void {
148
this.element.classList.add(CSS_INFINITE_LONG_RUNNING);
149
}
150
151
/**
152
* Tells the progress bar the total number of work. Use in combination with workedVal() to let
153
* the progress bar show the actual progress based on the work that is done.
154
*/
155
total(value: number): ProgressBar {
156
this.workedVal = 0;
157
this.totalWork = value;
158
this.element.setAttribute('aria-valuemax', value.toString());
159
160
return this;
161
}
162
163
/**
164
* Finds out if this progress bar is configured with total work
165
*/
166
hasTotal(): boolean {
167
return isNumber(this.totalWork);
168
}
169
170
/**
171
* Tells the progress bar that an increment of work has been completed.
172
*/
173
worked(value: number): ProgressBar {
174
value = Math.max(1, Number(value));
175
176
return this.doSetWorked(this.workedVal + value);
177
}
178
179
/**
180
* Tells the progress bar the total amount of work that has been completed.
181
*/
182
setWorked(value: number): ProgressBar {
183
value = Math.max(1, Number(value));
184
185
return this.doSetWorked(value);
186
}
187
188
private doSetWorked(value: number): ProgressBar {
189
const totalWork = this.totalWork || 100;
190
191
this.workedVal = value;
192
this.workedVal = Math.min(totalWork, this.workedVal);
193
194
this.element.classList.remove(CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DONE);
195
this.element.classList.add(CSS_ACTIVE, CSS_DISCRETE);
196
this.element.setAttribute('aria-valuenow', value.toString());
197
198
this.bit.style.width = 100 * (this.workedVal / (totalWork)) + '%';
199
200
return this;
201
}
202
203
getContainer(): HTMLElement {
204
return this.element;
205
}
206
207
show(delay?: number): void {
208
this.showDelayedScheduler.cancel();
209
this.progressSignal.value = getProgressAcccessibilitySignalScheduler(ProgressBar.PROGRESS_SIGNAL_DEFAULT_DELAY);
210
211
if (typeof delay === 'number') {
212
this.showDelayedScheduler.schedule(delay);
213
} else {
214
show(this.element);
215
}
216
}
217
218
hide(): void {
219
hide(this.element);
220
221
this.showDelayedScheduler.cancel();
222
this.progressSignal.clear();
223
}
224
}
225
226