Add wizard sidebar navigation (#1911)

This commit is contained in:
Matt Irvine
2018-07-12 11:15:56 -07:00
committed by GitHub
parent 05d0a89655
commit be45905830
5 changed files with 212 additions and 7 deletions

View File

@@ -13,6 +13,7 @@ import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { DialogContainer } from 'sql/platform/dialog/dialogContainer.component';
import { WizardNavigation } from 'sql/platform/dialog/wizardNavigation.component';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelViewContent } from 'sql/parts/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
@@ -37,12 +38,13 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
SelectBox,
InputBox,
DialogContainer,
WizardNavigation,
ModelViewContent,
ModelComponentWrapper,
ComponentHostDirective,
...extensionComponents
],
entryComponents: [DialogContainer, ...extensionComponents],
entryComponents: [DialogContainer, WizardNavigation, ...extensionComponents],
imports: [
FormsModule,
CommonModule,
@@ -65,7 +67,8 @@ export const DialogModule = (params, selector: string, instantiationService: IIn
}
ngDoBootstrap(appRef: ApplicationRef) {
const factoryWrapper: any = this._resolver.resolveComponentFactory(DialogContainer);
let componentClass = this.selector.startsWith(WizardNavigation.SELECTOR) ? WizardNavigation : DialogContainer;
const factoryWrapper: any = this._resolver.resolveComponentFactory<WizardNavigation | DialogContainer>(componentClass);
factoryWrapper.factory.selector = this.selector;
appRef.bootstrap(factoryWrapper);
}

View File

@@ -5,7 +5,7 @@
.dialogModal-body {
display: flex;
flex-direction: column;
flex-direction: row;
height: 100%;
width: 100%;
min-width: 500px;

View File

@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.wizardNavigation-container {
display: flex;
flex-direction: column;
width: 80px;
height: calc(100% + 25px);
margin-top: -25px;
}
.hc-black .wizardNavigation-container {
border-right-color: #2b56f2;
border-right-style: solid;
border-right-width: 1px;
background-color: unset;
}
.wizardNavigation-pageNumber {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-height: 130px;
}
.wizardNavigation-pageNumber a {
text-decoration: none;
}
.wizardNavigation-dot {
height: 30px;
width: 30px;
background-color: rgb(200, 200, 200);
color: white;
border-radius: 50%;
border-style: none;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
}
.hc-black .wizardNavigation-dot {
flex-grow: 1;
background-color: unset;
border-style: solid;
border-width: 1px;
border-color: white;
}
.wizardNavigation-connector {
width: 3px;
display: inline-block;
flex-grow: 1;
background-color: rgb(200, 200, 200);
}
.hc-black .wizardNavigation-connector {
display: none;
}
.wizardNavigation-connector.active,
.wizardNavigation-dot.active {
background-color: rgb(9, 109, 201);
}
.hc-black .wizardNavigation-dot.active {
border-color: #2b56f2;
background-color: unset;
border-style: solid;
}
.hc-black .wizardNavigation-dot.active:hover,
.hc-black .wizardNavigation-dot.active.currentPage:hover {
border-color: #F38518;
border-style: dashed;
}
.wizardNavigation-dot.active.currentPage {
border-style: double;
}
.hc-black .wizardNavigation-dot.active.currentPage {
border-style: solid;
border-color: #F38518;
}
.wizardNavigation-connector.invisible {
visibility: hidden;
}

View File

@@ -8,23 +8,22 @@
import 'vs/css!./media/dialogModal';
import { Modal, IModalOptions } from 'sql/base/browser/ui/modal/modal';
import { attachModalDialogStyler } from 'sql/common/theme/styler';
import { Wizard, Dialog, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
import { DialogModule } from 'sql/platform/dialog/dialog.module';
import { Button } from 'vs/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { Builder } from 'vs/base/browser/builder';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { localize } from 'vs/nls';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
export class WizardModal extends Modal {
private _dialogPanes = new Map<WizardPage, DialogPane>();
@@ -128,6 +127,8 @@ export class WizardModal extends Modal {
this._body = bodyBuilder.getHTMLElement();
});
this.initializeNavigation(this._body);
this._wizard.pages.forEach(page => {
this.registerPage(page);
});
@@ -201,6 +202,22 @@ export class WizardModal extends Modal {
}
}
/**
* Bootstrap angular for the wizard's left nav bar
*/
private initializeNavigation(bodyContainer: HTMLElement) {
bootstrapAngular(this._instantiationService,
DialogModule,
bodyContainer,
'wizard-navigation',
{
wizard: this._wizard,
navigationHandler: (index: number) => this.showPage(index, index > this._wizard.currentPage)
},
undefined,
() => undefined);
}
public open(): void {
this.showPage(0, false);
this.show();

View File

@@ -0,0 +1,91 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/wizardNavigation';
import { Component, Inject, forwardRef, ElementRef, AfterViewInit, ChangeDetectorRef, ViewChild } from '@angular/core';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { Event, Emitter } from 'vs/base/common/event';
import { Wizard } from './dialogTypes';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachStyler } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
export class WizardNavigationParams implements IBootstrapParams {
wizard: Wizard;
navigationHandler: (index: number) => void;
}
@Component({
selector: WizardNavigation.SELECTOR,
providers: [],
template: `
<div #container class="wizardNavigation-container">
<ng-container *ngFor="let item of _params.wizard.pages; let i = index">
<div class="wizardNavigation-pageNumber">
<div class="wizardNavigation-connector" [ngClass]="{'invisible': !hasTopConnector(i), 'active': isActive(i)}"></div>
<a [attr.href]="isActive(i) ? '' : null" [title]="item.title">
<span class="wizardNavigation-dot" [ngClass]="{'active': isActive(i), 'currentPage': isCurrentPage(i)}" (click)="navigate(i)">{{i+1}}</span>
</a>
<div class="wizardNavigation-connector" [ngClass]="{'invisible': !hasBottomConnector(i), 'active': isActive(i)}"></div>
</div>
</ng-container>
</div>
`
})
export class WizardNavigation implements AfterViewInit {
public static readonly SELECTOR = 'wizard-navigation';
private _onResize = new Emitter<void>();
public readonly onResize: Event<void> = this._onResize.event;
@ViewChild('container', { read: ElementRef }) private _container: ElementRef;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
@Inject(IBootstrapParams) private _params: WizardNavigationParams,
@Inject(IWorkbenchThemeService) private _themeService: IWorkbenchThemeService) {
}
ngAfterViewInit() {
this._themeService.onThemeChange(() => this.style());
this.style();
this._params.wizard.onPageChanged(() => this._changeRef.detectChanges());
}
hasTopConnector(index: number): boolean {
return index > 0;
}
hasBottomConnector(index: number): boolean {
return index + 1 !== this._params.wizard.pages.length;
}
isActive(index: number): boolean {
return index <= this._params.wizard.currentPage;
}
isCurrentPage(index: number): boolean {
return index === this._params.wizard.currentPage;
}
navigate(index: number): void {
if (this.isActive(index)) {
this._params.navigationHandler(index);
}
}
private style(): void {
let theme = this._themeService.getTheme();
let navigationBackgroundColor = theme.getColor(SIDE_BAR_BACKGROUND);
if (theme.type === 'light') {
navigationBackgroundColor = navigationBackgroundColor.lighten(0.03);
} else if (theme.type === 'dark') {
navigationBackgroundColor = navigationBackgroundColor.darken(0.03);
}
(this._container.nativeElement as HTMLElement).style.backgroundColor = navigationBackgroundColor.toString();
}
}