-
Notifications
You must be signed in to change notification settings - Fork 6.8k
feat(stepper): Merge initial prototype of stepper into the upstream stepper branch. #5742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 30 commits
33c6299
366b4c3
d8e3075
f94d966
23bfd0c
63d141e
471df0f
1d6ebb2
03e5849
c1cb73f
e90c839
992a07a
9e5720a
66bec54
617c270
42c1f14
b879732
f89b8bc
a529bf3
34ac69e
aee0abf
fcb94ff
5e9085f
f417435
36ad2cc
836c0ac
78c9eb5
26f73f9
a56e57f
3c1f4fd
0c3f7c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /** | ||
| * @license | ||
| * Copyright Google Inc. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by an MIT-style license that can be | ||
| * found in the LICENSE file at https://angular.io/license | ||
| */ | ||
|
|
||
| import {NgModule} from '@angular/core'; | ||
| import {CdkStepper, CdkStep} from './stepper'; | ||
| import {CommonModule} from '@angular/common'; | ||
| import {CdkStepLabel} from './step-label'; | ||
|
|
||
| @NgModule({ | ||
| imports: [CommonModule], | ||
| exports: [CdkStep, CdkStepper, CdkStepLabel], | ||
| declarations: [CdkStep, CdkStepper, CdkStepLabel] | ||
| }) | ||
| export class CdkStepperModule {} | ||
|
|
||
| export * from './stepper'; | ||
| export * from './step-label'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| /** | ||
| * @license | ||
| * Copyright Google Inc. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by an MIT-style license that can be | ||
| * found in the LICENSE file at https://angular.io/license | ||
| */ | ||
|
|
||
| import {Directive, TemplateRef} from '@angular/core'; | ||
|
|
||
| @Directive({ | ||
| selector: '[cdkStepLabel]', | ||
| }) | ||
| export class CdkStepLabel { | ||
| constructor(public template: TemplateRef<any>) { } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <ng-template><ng-content></ng-content></ng-template> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| /** | ||
| * @license | ||
| * Copyright Google Inc. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by an MIT-style license that can be | ||
| * found in the LICENSE file at https://angular.io/license | ||
| */ | ||
|
|
||
| import { | ||
| ContentChildren, | ||
| EventEmitter, | ||
| Input, | ||
| Output, | ||
| QueryList, | ||
| Directive, | ||
| // This import is only used to define a generic type. The current TypeScript version incorrectly | ||
| // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) | ||
| // tslint:disable-next-line:no-unused-variable | ||
| ElementRef, | ||
| Component, | ||
| ContentChild, | ||
| ViewChild, | ||
| TemplateRef | ||
| } from '@angular/core'; | ||
| import {LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE} from '../keyboard/keycodes'; | ||
| import {CdkStepLabel} from './step-label'; | ||
|
|
||
| /** Used to generate unique ID for each stepper component. */ | ||
| let nextId = 0; | ||
|
|
||
| /** Change event emitted on selection changes. */ | ||
| export class CdkStepperSelectionEvent { | ||
| /** Index of the step now selected. */ | ||
| selectedIndex: number; | ||
|
|
||
| /** Index of the step previously selected. */ | ||
| previouslySelectedIndex: number; | ||
|
|
||
| /** The step instance now selected. */ | ||
| selectedStep: CdkStep; | ||
|
|
||
| /** The step instance previously selected. */ | ||
| previouslySelectedStep: CdkStep; | ||
| } | ||
|
|
||
| @Component({ | ||
| selector: 'cdk-step', | ||
| templateUrl: 'step.html', | ||
| }) | ||
| export class CdkStep { | ||
| /** Template for step label if it exists. */ | ||
| @ContentChild(CdkStepLabel) stepLabel: CdkStepLabel; | ||
|
|
||
| /** Template for step content. */ | ||
| @ViewChild(TemplateRef) content: TemplateRef<any>; | ||
|
|
||
| /** Label of the step. */ | ||
| @Input() | ||
| label: string; | ||
|
|
||
| constructor(private _stepper: CdkStepper) { } | ||
|
|
||
| /** Selects this step component. */ | ||
| select(): void { | ||
| this._stepper.selected = this; | ||
| } | ||
| } | ||
|
|
||
| @Directive({ | ||
| selector: 'cdk-stepper', | ||
| host: { | ||
| '(focus)': '_focusStep()', | ||
| '(keydown)': '_onKeydown($event)', | ||
| }, | ||
| }) | ||
| export class CdkStepper { | ||
| /** The list of step components that the stepper is holding. */ | ||
| @ContentChildren(CdkStep) _steps: QueryList<CdkStep>; | ||
|
|
||
| /** The list of step headers of the steps in the stepper. */ | ||
| _stepHeader: QueryList<ElementRef>; | ||
|
|
||
| /** The index of the selected step. */ | ||
| @Input() | ||
| get selectedIndex() { return this._selectedIndex; } | ||
| set selectedIndex(index: number) { | ||
| if (this._selectedIndex != index) { | ||
| this._emitStepperSelectionEvent(index); | ||
| this._focusStep(this._selectedIndex); | ||
| } | ||
| } | ||
| private _selectedIndex: number = 0; | ||
|
|
||
| /** The step that is selected. */ | ||
| @Input() | ||
| get selected() { return this._steps[this.selectedIndex]; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same, should be |
||
| set selected(step: CdkStep) { | ||
| let index = this._steps.toArray().indexOf(step); | ||
| this.selectedIndex = index; | ||
| } | ||
|
|
||
| /** Event emitted when the selected step has changed. */ | ||
| @Output() selectionChange = new EventEmitter<CdkStepperSelectionEvent>(); | ||
|
|
||
| /** The index of the step that the focus can be set. */ | ||
| _focusIndex: number = 0; | ||
|
|
||
| /** Used to track unique ID for each stepper component. */ | ||
| private _groupId: number; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment what this is |
||
|
|
||
| constructor() { | ||
| this._groupId = nextId++; | ||
| } | ||
|
|
||
| /** Selects and focuses the next step in list. */ | ||
| next(): void { | ||
| this.selectedIndex = Math.min(this._selectedIndex + 1, this._steps.length - 1); | ||
| } | ||
|
|
||
| /** Selects and focuses the previous step in list. */ | ||
| previous(): void { | ||
| this.selectedIndex = Math.max(this._selectedIndex - 1, 0); | ||
| } | ||
|
|
||
| /** Returns a unique id for each step label element. */ | ||
| _getStepLabelId(i: number): string { | ||
| return `mat-step-label-${this._groupId}-${i}`; | ||
| } | ||
|
|
||
| /** Returns nique id for each step content element. */ | ||
| _getStepContentId(i: number): string { | ||
| return `mat-step-content-${this._groupId}-${i}`; | ||
| } | ||
|
|
||
| private _emitStepperSelectionEvent(newIndex: number): void { | ||
| const stepsArray = this._steps.toArray(); | ||
| this.selectionChange.emit({ | ||
| selectedIndex: newIndex, | ||
| previouslySelectedIndex: this._selectedIndex, | ||
| selectedStep: stepsArray[newIndex], | ||
| previouslySelectedStep: stepsArray[this._selectedIndex], | ||
| }); | ||
| this._selectedIndex = newIndex; | ||
| } | ||
|
|
||
| _onKeydown(event: KeyboardEvent) { | ||
| switch (event.keyCode) { | ||
| case RIGHT_ARROW: | ||
| this._focusStep((this._focusIndex + 1) % this._steps.length); | ||
| break; | ||
| case LEFT_ARROW: | ||
| this._focusStep((this._focusIndex + this._steps.length - 1) % this._steps.length); | ||
| break; | ||
| case SPACE: | ||
| case ENTER: | ||
| this._emitStepperSelectionEvent(this._focusIndex); | ||
| break; | ||
| default: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment why we do this (so we don't preventDefaault on keys we don't handle) |
||
| // Return to avoid calling preventDefault on keys that are not explicitly handled. | ||
| return; | ||
| } | ||
| event.preventDefault(); | ||
| } | ||
|
|
||
| private _focusStep(index: number) { | ||
| this._focusIndex = index; | ||
| this._stepHeader.toArray()[this._focusIndex].nativeElement.focus(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <h2>Horizontal Stepper Demo</h2> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are ok for now, but we should eventually add examples that are more like what a developer would actually want to create. |
||
| <mat-horizontal-stepper> | ||
| <mat-step *ngFor="let step of steps" [label]="step.label"> | ||
| <md-input-container> | ||
| <input mdInput placeholder="Answer" [(ngModel)]="step.content"> | ||
| </md-input-container> | ||
| </mat-step> | ||
| </mat-horizontal-stepper> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noticed that the stepper selectors need to be added to the compatibility mode list as well |
||
|
|
||
| <h2>Horizontal Stepper Demo with Templated Label</h2> | ||
| <mat-horizontal-stepper> | ||
| <mat-step *ngFor="let step of steps"> | ||
| <ng-template matStepLabel>{{step.label}}</ng-template> | ||
| <md-input-container> | ||
| <input mdInput placeholder="Answer" [(ngModel)]="step.content"> | ||
| </md-input-container> | ||
| </mat-step> | ||
| </mat-horizontal-stepper> | ||
|
|
||
| <h2>Vertical Stepper Demo</h2> | ||
| <mat-vertical-stepper> | ||
| <mat-step *ngFor="let step of steps" [label]="step.label"> | ||
| <md-input-container> | ||
| <input mdInput placeholder="Answer" [(ngModel)]="step.content"> | ||
| </md-input-container> | ||
| </mat-step> | ||
| </mat-vertical-stepper> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import {Component} from '@angular/core'; | ||
|
|
||
| @Component({ | ||
| moduleId: module.id, | ||
| selector: 'stepper-demo', | ||
| templateUrl: 'stepper-demo.html', | ||
| styleUrls: ['stepper-demo.scss'], | ||
| }) | ||
| export class StepperDemo { | ||
| steps = [ | ||
| {label: 'Confirm your name', content: 'Last name, First name.'}, | ||
| {label: 'Confirm your contact information', content: '123-456-7890'}, | ||
| {label: 'Confirm your address', content: '1600 Amphitheater Pkwy MTV'}, | ||
| {label: 'You are now done', content: 'Finished!'} | ||
| ]; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /** | ||
| * @license | ||
| * Copyright Google Inc. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by an MIT-style license that can be | ||
| * found in the LICENSE file at https://angular.io/license | ||
| */ | ||
|
|
||
| import {NgModule} from '@angular/core'; | ||
| import {CommonModule} from '@angular/common'; | ||
| import {PortalModule} from '@angular/cdk'; | ||
| import {MdButtonModule} from '../button/index'; | ||
| import {MdHorizontalStepper} from './stepper-horizontal'; | ||
| import {MdVerticalStepper} from './stepper-vertical'; | ||
| import {MdStep} from './step'; | ||
| import {MdStepper} from './stepper'; | ||
| import {CdkStepperModule} from '@angular/cdk'; | ||
| import {MdCommonModule} from '../core'; | ||
| import {MdStepLabel} from './step-label'; | ||
|
|
||
| @NgModule({ | ||
| imports: [MdCommonModule, CommonModule, PortalModule, MdButtonModule, CdkStepperModule], | ||
| exports: [MdCommonModule, MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper], | ||
| declarations: [MdHorizontalStepper, MdVerticalStepper, MdStep, MdStepLabel, MdStepper], | ||
| }) | ||
| export class MdStepperModule {} | ||
|
|
||
| export * from './stepper-horizontal'; | ||
| export * from './stepper-vertical'; | ||
| export * from './step'; | ||
| export * from './step-label'; | ||
| export * from './stepper'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** | ||
| * @license | ||
| * Copyright Google Inc. All Rights Reserved. | ||
| * | ||
| * Use of this source code is governed by an MIT-style license that can be | ||
| * found in the LICENSE file at https://angular.io/license | ||
| */ | ||
|
|
||
| import {Directive, TemplateRef} from '@angular/core'; | ||
| import {CdkStepLabel} from '@angular/cdk'; | ||
|
|
||
| @Directive({ | ||
| selector: '[mdStepLabel], [matStepLabel]', | ||
| }) | ||
| export class MdStepLabel extends CdkStepLabel { | ||
| constructor(template: TemplateRef<any>) { | ||
| super(template); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <ng-template><ng-content></ng-content></ng-template> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we want this to be an
@Input()right?