diff --git a/src/lib/stepper/step-header.html b/src/lib/stepper/step-header.html index fac0b329b18c..2d35efdba267 100644 --- a/src/lib/stepper/step-header.html +++ b/src/lib/stepper/step-header.html @@ -3,15 +3,27 @@ [class.mat-step-icon-not-touched]="state == 'number' && !selected" [ngSwitch]="state"> - {{index + 1}} + + + {{index + 1}} + - + create - + done diff --git a/src/lib/stepper/step-header.ts b/src/lib/stepper/step-header.ts index 13157c34e7a7..66adfff9f0a5 100644 --- a/src/lib/stepper/step-header.ts +++ b/src/lib/stepper/step-header.ts @@ -20,6 +20,7 @@ import { import {Subscription} from 'rxjs/Subscription'; import {MatStepLabel} from './step-label'; import {MatStepperIntl} from './stepper-intl'; +import {MatStepperIconContext} from './stepper-icon'; @Component({ @@ -44,7 +45,7 @@ export class MatStepHeader implements OnDestroy { @Input() label: MatStepLabel | string; /** Overrides for the header icons, passed in via the stepper. */ - @Input() iconOverrides: {[key: string]: TemplateRef}; + @Input() iconOverrides: {[key: string]: TemplateRef}; /** Index of the given step. */ @Input() index: number; @@ -87,6 +88,15 @@ export class MatStepHeader implements OnDestroy { return this._element.nativeElement; } + /** Template context variables that are exposed to the `matStepperIcon` instances. */ + _getIconContext(): MatStepperIconContext { + return { + index: this.index, + active: this.active, + optional: this.optional + }; + } + focus() { this._getHostElement().focus(); } diff --git a/src/lib/stepper/stepper-icon.ts b/src/lib/stepper/stepper-icon.ts index b2bf79fe14d1..c8cc8556072e 100644 --- a/src/lib/stepper/stepper-icon.ts +++ b/src/lib/stepper/stepper-icon.ts @@ -8,6 +8,16 @@ import {Directive, Input, TemplateRef} from '@angular/core'; +/** Template context available to an attached `matStepperIcon`. */ +export interface MatStepperIconContext { + /** Index of the step. */ + index: number; + /** Whether the step is currently active. */ + active: boolean; + /** Whether the step is optional. */ + optional: boolean; +} + /** * Template to be used to override the icons inside the step header. */ @@ -16,7 +26,7 @@ import {Directive, Input, TemplateRef} from '@angular/core'; }) export class MatStepperIcon { /** Name of the icon to be overridden. */ - @Input('matStepperIcon') name: 'edit' | 'done'; + @Input('matStepperIcon') name: 'edit' | 'done' | 'number'; - constructor(public templateRef: TemplateRef) { } + constructor(public templateRef: TemplateRef) {} } diff --git a/src/lib/stepper/stepper.md b/src/lib/stepper/stepper.md index 37ac9a30d3b4..d8fafef1eef3 100644 --- a/src/lib/stepper/stepper.md +++ b/src/lib/stepper/stepper.md @@ -124,7 +124,8 @@ this default `completed` behavior by setting the `completed` attribute as needed #### Overriding icons By default, the step headers will use the `create` and `done` icons from the Material design icon set via `` elements. If you want to provide a different set of icons, you can do so -by placing a `matStepperIcon` for each of the icons that you want to override: +by placing a `matStepperIcon` for each of the icons that you want to override. The `index`, +`active`, and `optional` values of the individual steps are available through template variables: ```html @@ -136,6 +137,11 @@ by placing a `matStepperIcon` for each of the icons that you want to override: done_all + + + {{index + 10}} + + ``` diff --git a/src/lib/stepper/stepper.spec.ts b/src/lib/stepper/stepper.spec.ts index e3198496c70c..57c964336512 100644 --- a/src/lib/stepper/stepper.spec.ts +++ b/src/lib/stepper/stepper.spec.ts @@ -354,6 +354,13 @@ describe('MatStepper', () => { expect(header.textContent).toContain('Custom done'); }); + + it('should allow for the `number` icon to be overridden with context', () => { + const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper)); + const headers = stepperDebugElement.nativeElement.querySelectorAll('mat-step-header'); + + expect(headers[2].textContent).toContain('III'); + }); }); describe('RTL', () => { @@ -1025,6 +1032,9 @@ class SimpleStepperWithStepControlAndCompletedBinding { Custom edit Custom done + + {{getRomanNumeral(index + 1)}} + Content 1 Content 2 @@ -1032,7 +1042,21 @@ class SimpleStepperWithStepControlAndCompletedBinding { ` }) -class IconOverridesStepper {} +class IconOverridesStepper { + getRomanNumeral(value: number) { + return { + 1: 'I', + 2: 'II', + 3: 'III', + 4: 'IV', + 5: 'V', + 6: 'VI', + 7: 'VII', + 8: 'VIII', + 9: 'IX' + }[value]; + } +} @Component({ template: ` diff --git a/src/lib/stepper/stepper.ts b/src/lib/stepper/stepper.ts index 3efd2f86121d..b51f2c78b1dc 100644 --- a/src/lib/stepper/stepper.ts +++ b/src/lib/stepper/stepper.ts @@ -31,7 +31,7 @@ import {takeUntil} from 'rxjs/operators/takeUntil'; import {MatStepHeader} from './step-header'; import {MatStepLabel} from './step-label'; import {matStepperAnimations} from './stepper-animations'; -import {MatStepperIcon} from './stepper-icon'; +import {MatStepperIcon, MatStepperIconContext} from './stepper-icon'; /** Workaround for https://github.com/angular/angular/issues/17849 */ export const _MatStep = CdkStep; @@ -83,20 +83,18 @@ export class MatStepper extends CdkStepper implements AfterContentInit { @ContentChildren(MatStepperIcon) _icons: QueryList; /** Consumer-specified template-refs to be used to override the header icons. */ - _iconOverrides: {[key: string]: TemplateRef} = {}; + _iconOverrides: {[key: string]: TemplateRef} = {}; ngAfterContentInit() { const icons = this._icons.toArray(); - const editOverride = icons.find(icon => icon.name === 'edit'); - const doneOverride = icons.find(icon => icon.name === 'done'); - if (editOverride) { - this._iconOverrides.edit = editOverride.templateRef; - } + ['edit', 'done', 'number'].forEach(name => { + const override = icons.find(icon => icon.name === name); - if (doneOverride) { - this._iconOverrides.done = doneOverride.templateRef; - } + if (override) { + this._iconOverrides[name] = override.templateRef; + } + }); // Mark the component for change detection whenever the content children query changes this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => this._stateChanged());