Path: blob/main/src/vs/base/browser/ui/progressbar/progressbar.ts
3296 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 { hide, show } from '../../dom.js';6import { getProgressAcccessibilitySignalScheduler } from './progressAccessibilitySignal.js';7import { RunOnceScheduler } from '../../../common/async.js';8import { Disposable, IDisposable, MutableDisposable } from '../../../common/lifecycle.js';9import { isNumber } from '../../../common/types.js';10import './progressbar.css';1112const CSS_DONE = 'done';13const CSS_ACTIVE = 'active';14const CSS_INFINITE = 'infinite';15const CSS_INFINITE_LONG_RUNNING = 'infinite-long-running';16const CSS_DISCRETE = 'discrete';1718export interface IProgressBarOptions extends IProgressBarStyles {19}2021export interface IProgressBarStyles {22progressBarBackground: string | undefined;23}2425export const unthemedProgressBarOptions: IProgressBarOptions = {26progressBarBackground: undefined27};2829/**30* A progress bar with support for infinite or discrete progress.31*/32export class ProgressBar extends Disposable {3334/**35* After a certain time of showing the progress bar, switch36* to long-running mode and throttle animations to reduce37* the pressure on the GPU process.38*39* https://github.com/microsoft/vscode/issues/9790040* https://github.com/microsoft/vscode/issues/13839641*/42private static readonly LONG_RUNNING_INFINITE_THRESHOLD = 10000;4344private static readonly PROGRESS_SIGNAL_DEFAULT_DELAY = 3000;4546private workedVal: number;47private element!: HTMLElement;48private bit!: HTMLElement;49private totalWork: number | undefined;50private showDelayedScheduler: RunOnceScheduler;51private longRunningScheduler: RunOnceScheduler;52private readonly progressSignal = this._register(new MutableDisposable<IDisposable>());5354constructor(container: HTMLElement, options?: IProgressBarOptions) {55super();5657this.workedVal = 0;5859this.showDelayedScheduler = this._register(new RunOnceScheduler(() => show(this.element), 0));60this.longRunningScheduler = this._register(new RunOnceScheduler(() => this.infiniteLongRunning(), ProgressBar.LONG_RUNNING_INFINITE_THRESHOLD));6162this.create(container, options);63}6465private create(container: HTMLElement, options?: IProgressBarOptions): void {66this.element = document.createElement('div');67this.element.classList.add('monaco-progress-container');68this.element.setAttribute('role', 'progressbar');69this.element.setAttribute('aria-valuemin', '0');70container.appendChild(this.element);7172this.bit = document.createElement('div');73this.bit.classList.add('progress-bit');74this.bit.style.backgroundColor = options?.progressBarBackground || '#0E70C0';75this.element.appendChild(this.bit);76}7778private off(): void {79this.bit.style.width = 'inherit';80this.bit.style.opacity = '1';81this.element.classList.remove(CSS_ACTIVE, CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DISCRETE);8283this.workedVal = 0;84this.totalWork = undefined;8586this.longRunningScheduler.cancel();87this.progressSignal.clear();88}8990/**91* Indicates to the progress bar that all work is done.92*/93done(): ProgressBar {94return this.doDone(true);95}9697/**98* Stops the progressbar from showing any progress instantly without fading out.99*/100stop(): ProgressBar {101return this.doDone(false);102}103104private doDone(delayed: boolean): ProgressBar {105this.element.classList.add(CSS_DONE);106107// discrete: let it grow to 100% width and hide afterwards108if (!this.element.classList.contains(CSS_INFINITE)) {109this.bit.style.width = 'inherit';110111if (delayed) {112setTimeout(() => this.off(), 200);113} else {114this.off();115}116}117118// infinite: let it fade out and hide afterwards119else {120this.bit.style.opacity = '0';121if (delayed) {122setTimeout(() => this.off(), 200);123} else {124this.off();125}126}127128return this;129}130131/**132* Use this mode to indicate progress that has no total number of work units.133*/134infinite(): ProgressBar {135this.bit.style.width = '2%';136this.bit.style.opacity = '1';137138this.element.classList.remove(CSS_DISCRETE, CSS_DONE, CSS_INFINITE_LONG_RUNNING);139this.element.classList.add(CSS_ACTIVE, CSS_INFINITE);140141this.longRunningScheduler.schedule();142143return this;144}145146private infiniteLongRunning(): void {147this.element.classList.add(CSS_INFINITE_LONG_RUNNING);148}149150/**151* Tells the progress bar the total number of work. Use in combination with workedVal() to let152* the progress bar show the actual progress based on the work that is done.153*/154total(value: number): ProgressBar {155this.workedVal = 0;156this.totalWork = value;157this.element.setAttribute('aria-valuemax', value.toString());158159return this;160}161162/**163* Finds out if this progress bar is configured with total work164*/165hasTotal(): boolean {166return isNumber(this.totalWork);167}168169/**170* Tells the progress bar that an increment of work has been completed.171*/172worked(value: number): ProgressBar {173value = Math.max(1, Number(value));174175return this.doSetWorked(this.workedVal + value);176}177178/**179* Tells the progress bar the total amount of work that has been completed.180*/181setWorked(value: number): ProgressBar {182value = Math.max(1, Number(value));183184return this.doSetWorked(value);185}186187private doSetWorked(value: number): ProgressBar {188const totalWork = this.totalWork || 100;189190this.workedVal = value;191this.workedVal = Math.min(totalWork, this.workedVal);192193this.element.classList.remove(CSS_INFINITE, CSS_INFINITE_LONG_RUNNING, CSS_DONE);194this.element.classList.add(CSS_ACTIVE, CSS_DISCRETE);195this.element.setAttribute('aria-valuenow', value.toString());196197this.bit.style.width = 100 * (this.workedVal / (totalWork)) + '%';198199return this;200}201202getContainer(): HTMLElement {203return this.element;204}205206show(delay?: number): void {207this.showDelayedScheduler.cancel();208this.progressSignal.value = getProgressAcccessibilitySignalScheduler(ProgressBar.PROGRESS_SIGNAL_DEFAULT_DELAY);209210if (typeof delay === 'number') {211this.showDelayedScheduler.schedule(delay);212} else {213show(this.element);214}215}216217hide(): void {218hide(this.element);219220this.showDelayedScheduler.cancel();221this.progressSignal.clear();222}223}224225226