extensionsWidgets.ts 10.5 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import 'vs/css!./media/extensionsWidgets';
7
import { Disposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
S
Sandeep Somavarapu 已提交
8
import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs/workbench/contrib/extensions/common/extensions';
9 10 11
import { append, $, addClass } from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
12
import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
13
import { ILabelService } from 'vs/platform/label/common/label';
S
Sandeep Somavarapu 已提交
14
import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
M
Matt Bierner 已提交
15
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
S
Sandeep Somavarapu 已提交
16
import { EXTENSION_BADGE_REMOTE_BACKGROUND, EXTENSION_BADGE_REMOTE_FOREGROUND } from 'vs/workbench/common/theme';
17
import { Emitter, Event } from 'vs/base/common/event';
18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
19 20

export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
S
Sandeep Somavarapu 已提交
21 22 23
	private _extension: IExtension | null = null;
	get extension(): IExtension | null { return this._extension; }
	set extension(extension: IExtension | null) { this._extension = extension; this.update(); }
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
	update(): void { this.render(); }
	abstract render(): void;
}

export class Label extends ExtensionWidget {

	constructor(
		private element: HTMLElement,
		private fn: (extension: IExtension) => string,
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
		super();
		this.render();
	}

	render(): void {
		this.element.textContent = this.extension ? this.fn(this.extension) : '';
	}
}

export class InstallCountWidget extends ExtensionWidget {

	constructor(
		private container: HTMLElement,
		private small: boolean,
		@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService
	) {
		super();
		addClass(container, 'extension-install-count');
		this.render();
	}

	render(): void {
		this.container.innerHTML = '';

		if (!this.extension) {
			return;
		}

		const installCount = this.extension.installCount;

		if (installCount === undefined) {
			return;
		}

		let installLabel: string;

		if (this.small) {
			if (installCount > 1000000) {
				installLabel = `${Math.floor(installCount / 100000) / 10}M`;
			} else if (installCount > 1000) {
				installLabel = `${Math.floor(installCount / 1000)}K`;
			} else {
				installLabel = String(installCount);
			}
		}
		else {
			installLabel = installCount.toLocaleString(platform.locale);
		}

84
		append(this.container, $('span.codicon.codicon-cloud-download'));
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
		const count = append(this.container, $('span.count'));
		count.textContent = installLabel;
	}
}

export class RatingsWidget extends ExtensionWidget {

	constructor(
		private container: HTMLElement,
		private small: boolean
	) {
		super();
		addClass(container, 'extension-ratings');

		if (this.small) {
			addClass(container, 'small');
		}

		this.render();
	}

	render(): void {
		this.container.innerHTML = '';

		if (!this.extension) {
			return;
		}

		if (this.extension.rating === undefined) {
			return;
		}

		if (this.small && !this.extension.ratingCount) {
			return;
		}

		const rating = Math.round(this.extension.rating * 2) / 2;

		if (this.small) {
124
			append(this.container, $('span.codicon.codicon-star-full'));
125 126 127 128 129 130

			const count = append(this.container, $('span.count'));
			count.textContent = String(rating);
		} else {
			for (let i = 1; i <= 5; i++) {
				if (rating >= i) {
131
					append(this.container, $('span.codicon.codicon-star-full'));
132
				} else if (rating >= i - 0.5) {
133
					append(this.container, $('span.codicon.codicon-star-half'));
134
				} else {
135
					append(this.container, $('span.codicon.codicon-star-empty'));
136 137 138
				}
			}
		}
S
Sandeep Somavarapu 已提交
139
		this.container.title = this.extension.ratingCount === 1 ? localize('ratedBySingleUser', "Rated by 1 user")
140
			: typeof this.extension.ratingCount === 'number' && this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('noRating', "No rating");
141 142 143
	}
}

144 145 146 147
export class TooltipWidget extends ExtensionWidget {

	constructor(
		private readonly parent: HTMLElement,
S
Sandeep Somavarapu 已提交
148
		private readonly tooltipAction: ExtensionToolTipAction,
149
		private readonly recommendationWidget: RecommendationWidget,
150
		@ILabelService private readonly labelService: ILabelService
151 152
	) {
		super();
153
		this._register(Event.any<any>(
S
Sandeep Somavarapu 已提交
154
			this.tooltipAction.onDidChange,
155 156
			this.recommendationWidget.onDidChangeTooltip,
			this.labelService.onDidChangeFormatters
157
		)(() => this.render()));
158 159 160 161 162
	}

	render(): void {
		this.parent.title = '';
		this.parent.removeAttribute('aria-label');
163
		this.parent.title = this.getTooltip();
164
		if (this.extension) {
165
			this.parent.setAttribute('aria-label', localize('extension-arialabel', "{0}. Press enter for extension details.", this.extension.displayName));
166 167 168
		}
	}

169 170 171 172
	private getTooltip(): string {
		if (!this.extension) {
			return '';
		}
S
Sandeep Somavarapu 已提交
173 174
		if (this.tooltipAction.label) {
			return this.tooltipAction.label;
175 176 177 178 179 180
		}
		return this.recommendationWidget.tooltip;
	}

}

181 182
export class RecommendationWidget extends ExtensionWidget {

M
Matt Bierner 已提交
183
	private element?: HTMLElement;
184
	private readonly disposables = this._register(new DisposableStore());
185

S
Sandeep Somavarapu 已提交
186
	private _tooltip: string = '';
187 188 189 190 191 192 193 194 195 196
	get tooltip(): string { return this._tooltip; }
	set tooltip(tooltip: string) {
		if (this._tooltip !== tooltip) {
			this._tooltip = tooltip;
			this._onDidChangeTooltip.fire();
		}
	}
	private _onDidChangeTooltip: Emitter<void> = this._register(new Emitter<void>());
	readonly onDidChangeTooltip: Event<void> = this._onDidChangeTooltip.event;

197 198 199 200 201 202 203 204
	constructor(
		private parent: HTMLElement,
		@IThemeService private readonly themeService: IThemeService,
		@IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService
	) {
		super();
		this.render();
		this._register(toDisposable(() => this.clear()));
S
Sandeep Somavarapu 已提交
205
		this._register(this.extensionTipsService.onRecommendationChange(() => this.render()));
206 207 208
	}

	private clear(): void {
209
		this.tooltip = '';
210 211 212 213
		this.parent.setAttribute('aria-label', this.extension ? localize('viewExtensionDetailsAria', "{0}. Press enter for extension details.", this.extension.displayName) : '');
		if (this.element) {
			this.parent.removeChild(this.element);
		}
M
Matt Bierner 已提交
214
		this.element = undefined;
215
		this.disposables.clear();
216 217 218 219 220 221 222
	}

	render(): void {
		this.clear();
		if (!this.extension) {
			return;
		}
S
Sandeep Somavarapu 已提交
223 224 225 226
		const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason();
		if (extRecommendations[this.extension.identifier.id.toLowerCase()]) {
			this.element = append(this.parent, $('div.bookmark'));
			const recommendation = append(this.element, $('.recommendation'));
227
			append(recommendation, $('span.codicon.codicon-star'));
M
Matt Bierner 已提交
228
			const applyBookmarkStyle = (theme: ITheme) => {
S
Sandeep Somavarapu 已提交
229 230 231 232 233 234 235
				const bgColor = theme.getColor(extensionButtonProminentBackground);
				const fgColor = theme.getColor(extensionButtonProminentForeground);
				recommendation.style.borderTopColor = bgColor ? bgColor.toString() : 'transparent';
				recommendation.style.color = fgColor ? fgColor.toString() : 'white';
			};
			applyBookmarkStyle(this.themeService.getTheme());
			this.themeService.onThemeChange(applyBookmarkStyle, this, this.disposables);
236
			this.tooltip = extRecommendations[this.extension.identifier.id.toLowerCase()].reasonText;
S
Sandeep Somavarapu 已提交
237
		}
238 239 240 241 242 243
	}

}

export class RemoteBadgeWidget extends ExtensionWidget {

244
	private readonly remoteBadge = this._register(new MutableDisposable<RemoteBadge>());
245

246 247
	private element: HTMLElement;

248
	constructor(
249
		parent: HTMLElement,
250
		private readonly tooltip: boolean,
251
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
252
		@IInstantiationService private readonly instantiationService: IInstantiationService
253 254
	) {
		super();
255
		this.element = append(parent, $('.extension-remote-badge-container'));
256 257 258 259 260
		this.render();
		this._register(toDisposable(() => this.clear()));
	}

	private clear(): void {
261 262
		if (this.remoteBadge.value) {
			this.element.removeChild(this.remoteBadge.value.element);
263
		}
264
		this.remoteBadge.clear();
265 266 267 268
	}

	render(): void {
		this.clear();
269
		if (!this.extension || !this.extension.local || !this.extension.server || !(this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) || this.extension.server !== this.extensionManagementServerService.remoteExtensionManagementServer) {
270 271
			return;
		}
272 273
		this.remoteBadge.value = this.instantiationService.createInstance(RemoteBadge, this.tooltip);
		append(this.element, this.remoteBadge.value.element);
274 275
	}
}
276 277 278 279 280 281

class RemoteBadge extends Disposable {

	readonly element: HTMLElement;

	constructor(
282
		private readonly tooltip: boolean,
283 284
		@ILabelService private readonly labelService: ILabelService,
		@IThemeService private readonly themeService: IThemeService,
285
		@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService
286 287 288 289 290 291 292
	) {
		super();
		this.element = $('div.extension-remote-badge');
		this.render();
	}

	private render(): void {
293
		append(this.element, $('span.codicon.codicon-remote'));
294 295 296 297 298

		const applyBadgeStyle = () => {
			if (!this.element) {
				return;
			}
S
Sandeep Somavarapu 已提交
299 300
			const bgColor = this.themeService.getTheme().getColor(EXTENSION_BADGE_REMOTE_BACKGROUND);
			const fgColor = this.themeService.getTheme().getColor(EXTENSION_BADGE_REMOTE_FOREGROUND);
301 302 303 304 305 306
			this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
			this.element.style.color = fgColor ? fgColor.toString() : '';
		};
		applyBadgeStyle();
		this._register(this.themeService.onThemeChange(() => applyBadgeStyle()));

307 308
		if (this.tooltip) {
			const updateTitle = () => {
309 310
				if (this.element && this.extensionManagementServerService.remoteExtensionManagementServer) {
					this.element.title = localize('remote extension title', "Extension in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label);
311 312 313 314 315
				}
			};
			this._register(this.labelService.onDidChangeFormatters(() => updateTitle()));
			updateTitle();
		}
316
	}
S
Sandeep Somavarapu 已提交
317
}