diff --git a/src/sql/workbench/browser/modelComponents/loadingSpinner.component.ts b/src/sql/workbench/browser/modelComponents/loadingSpinner.component.ts index 172438d5e3..2c09893a10 100644 --- a/src/sql/workbench/browser/modelComponents/loadingSpinner.component.ts +++ b/src/sql/workbench/browser/modelComponents/loadingSpinner.component.ts @@ -4,20 +4,44 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/loadingComponent'; -import { Component, Input } from '@angular/core'; - +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import * as nls from 'vs/nls'; +import { status } from 'vs/base/browser/ui/aria/aria'; + +const DefaultLoadingMessage = nls.localize('loadingMessage', "Loading"); +const DefaultLoadingCompletedMessage = nls.localize('loadingCompletedMessage', "Loading completed"); @Component({ selector: 'loading-spinner', template: `
-
+
` }) -export default class LoadingSpinner { - public readonly _loadingTitle = nls.localize('loadingMessage', "Loading"); +export default class LoadingSpinner implements OnChanges { - @Input() loading: boolean; + ngOnChanges(changes: SimpleChanges): void { + if (changes.loading !== undefined) { + const message = this.loading ? this._loadingMessage : this._loadingCompletedMessage; + status(message); + } + } + + get _loadingMessage(): string { + return this.loadingMessage ? this.loadingMessage : DefaultLoadingMessage; + } + + get _loadingCompletedMessage(): string { + return this.loadingCompletedMessage ? this.loadingCompletedMessage : DefaultLoadingCompletedMessage; + } + + @Input() + loading: boolean; + + @Input() + loadingMessage: string; + + @Input() + loadingCompletedMessage: string; } diff --git a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.html b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.html index 3d688ee76a..9809cd426e 100644 --- a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.html +++ b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.html @@ -7,6 +7,8 @@
+
diff --git a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts index 7750e4cb11..469c0d9b4c 100644 --- a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts @@ -6,7 +6,7 @@ import 'vs/css!sql/media/icons/common-icons'; import 'vs/css!./media/explorerWidget'; -import { Component, Inject, forwardRef, OnInit, ViewChild, ElementRef } from '@angular/core'; +import { Component, Inject, forwardRef, OnInit, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core'; import { Router } from '@angular/router'; import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget'; @@ -27,6 +27,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { subscriptionToDisposable } from 'sql/base/browser/lifecycle'; import { ObjectMetadataWrapper } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/objectMetadataWrapper'; +import { status, alert } from 'vs/base/browser/ui/aria/aria'; @Component({ selector: 'explorer-widget', @@ -47,6 +48,9 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, private _treeFilter = new ExplorerFilter(); private _inited = false; + public loading: boolean = false; + public loadingMessage: string; + public loadingCompletedMessage: string; @ViewChild('input') private _inputContainer: ElementRef; @ViewChild('table') private _tableContainer: ElementRef; @@ -59,9 +63,12 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, @Inject(IWorkbenchThemeService) private readonly themeService: IWorkbenchThemeService, @Inject(IContextViewService) private readonly contextViewService: IContextViewService, @Inject(IInstantiationService) private readonly instantiationService: IInstantiationService, - @Inject(ICapabilitiesService) private readonly capabilitiesService: ICapabilitiesService + @Inject(ICapabilitiesService) private readonly capabilitiesService: ICapabilitiesService, + @Inject(forwardRef(() => ChangeDetectorRef)) private readonly _cd: ChangeDetectorRef ) { super(); + this.loadingMessage = this._config.context === 'database' ? nls.localize('loadingObjects', "loading objects") : nls.localize('loadingDatabases', "loading databases"); + this.loadingCompletedMessage = this._config.context === 'database' ? nls.localize('loadingObjectsCompleted', "loading objects completed.") : nls.localize('loadingDatabasesCompleted', "loading databases completed."); this.init(); } @@ -76,9 +83,25 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, }; this._input = new InputBox(this._inputContainer.nativeElement, this.contextViewService, inputOptions); this._register(this._input.onDidChange(e => { - this._filterDelayer.trigger(() => { + this._filterDelayer.trigger(async () => { this._treeFilter.filterString = e; - this._tree.refresh(); + await this._tree.refresh(); + const navigator = this._tree.getNavigator(); + let item = navigator.next(); + let count = 0; + while (item) { + count++; + item = navigator.next(); + } + let message: string; + if (count === 0) { + message = nls.localize('explorerSearchNoMatchResultMessage', "No matching item found"); + } else if (count === 1) { + message = nls.localize('explorerSearchSingleMatchResultMessage', "Filtered search list to 1 item"); + } else { + message = nls.localize('explorerSearchMatchResultMessage', "Filtered search list to {0} items", count); + } + status(message); }); })); this._tree = new Tree(this._tableContainer.nativeElement, { @@ -95,6 +118,8 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, } private init(): void { + this.setLoadingStatus(true); + if (this._config.context === 'database') { this._register(subscriptionToDisposable(this._bootstrap.metadataService.metadata.subscribe( data => { @@ -103,10 +128,11 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, objectData.sort(ObjectMetadataWrapper.sort); this._treeDataSource.data = objectData; this._tree.setInput(new ExplorerModel()); + this.setLoadingStatus(false); } }, error => { - (this._el.nativeElement).innerText = nls.localize('dashboard.explorer.objectError', "Unable to load objects"); + this.showErrorMessage(nls.localize('dashboard.explorer.objectError', "Unable to load objects")); } ))); } else { @@ -122,9 +148,10 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, }); this._treeDataSource.data = profileData; this._tree.setInput(new ExplorerModel()); + this.setLoadingStatus(false); }, error => { - (this._el.nativeElement).innerText = nls.localize('dashboard.explorer.databaseError', "Unable to load databases"); + this.showErrorMessage(nls.localize('dashboard.explorer.databaseError', "Unable to load databases")); } ))); } @@ -139,4 +166,17 @@ export class ExplorerWidget extends DashboardWidget implements IDashboardWidget, this._tree.layout(getContentHeight(this._tableContainer.nativeElement)); } } + + private setLoadingStatus(loading: boolean): void { + this.loading = loading; + + if (this._inited) { + this._cd.detectChanges(); + } + } + + private showErrorMessage(message: string): void { + (this._el.nativeElement).innerText = message; + alert(message); + } }