refactor: replace color getters and setters with mixin (#4974)

* refactor: replace color getters and setters with mixin

* Replaces the color getters and setters with a Mixin function (similar as for the disabled property)
* This should reduce payload and also should make it way easier to maintain the color functionality.

* Address comments

* Updates
This commit is contained in:
Paul Gschwendtner
2017-06-07 23:56:28 +02:00
committed by Andrew Seguin
parent 28d2ddd098
commit 3779395ef6
14 changed files with 268 additions and 252 deletions
+6 -1
View File
@@ -38,6 +38,12 @@ describe('MdButton', () => {
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList.contains('mat-accent')).toBe(true);
expect(aDebugElement.nativeElement.classList.contains('mat-accent')).toBe(true);
testComponent.buttonColor = null;
fixture.detectChanges();
expect(buttonDebugElement.nativeElement.classList).not.toContain('mat-accent');
expect(aDebugElement.nativeElement.classList).not.toContain('mat-accent');
});
it('should should not clear previous defined classes', () => {
@@ -59,7 +65,6 @@ describe('MdButton', () => {
expect(buttonDebugElement.nativeElement.classList.contains('mat-primary')).toBe(false);
expect(buttonDebugElement.nativeElement.classList.contains('mat-accent')).toBe(true);
expect(buttonDebugElement.nativeElement.classList.contains('custom-class')).toBe(true);
});
// Regular button tests
+17 -38
View File
@@ -11,6 +11,7 @@ import {
} from '@angular/core';
import {coerceBooleanProperty, FocusOriginMonitor, Platform} from '../core';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
// TODO(kara): Convert attribute selectors to classes when attr maps become available
@@ -71,8 +72,10 @@ export class MdMiniFabCssMatStyler {}
// Boilerplate for applying mixins to MdButton.
export class MdButtonBase { }
export const _MdButtonMixinBase = mixinDisabled(MdButtonBase);
export class MdButtonBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdButtonMixinBase = mixinColor(mixinDisabled(MdButtonBase));
/**
@@ -89,13 +92,11 @@ export const _MdButtonMixinBase = mixinDisabled(MdButtonBase);
},
templateUrl: 'button.html',
styleUrls: ['button.css'],
inputs: ['disabled'],
inputs: ['disabled', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisable {
private _color: string;
export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisable, CanColor {
/** Whether the button is round. */
_isRoundButton: boolean = this._hasAttributeWithPrefix('fab', 'mini-fab');
@@ -110,12 +111,11 @@ export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisabl
get disableRipple() { return this._disableRipple; }
set disableRipple(v) { this._disableRipple = coerceBooleanProperty(v); }
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer2,
private _platform: Platform,
private _focusOriginMonitor: FocusOriginMonitor) {
super();
constructor(renderer: Renderer2,
elementRef: ElementRef,
private _platform: Platform,
private _focusOriginMonitor: FocusOriginMonitor) {
super(renderer, elementRef);
this._focusOriginMonitor.monitor(this._elementRef.nativeElement, this._renderer, true);
}
@@ -123,27 +123,6 @@ export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisabl
this._focusOriginMonitor.stopMonitoring(this._elementRef.nativeElement);
}
/** The color of the button. Can be `primary`, `accent`, or `warn`. */
@Input()
get color(): string { return this._color; }
set color(value: string) { this._updateColor(value); }
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
_setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
if (isAdd) {
this._renderer.addClass(this._getHostElement(), `mat-${color}`);
} else {
this._renderer.removeClass(this._getHostElement(), `mat-${color}`);
}
}
}
/** Focuses the button. */
focus(): void {
this._getHostElement().focus();
@@ -189,18 +168,18 @@ export class MdButton extends _MdButtonMixinBase implements OnDestroy, CanDisabl
'[attr.aria-disabled]': '_isAriaDisabled',
'(click)': '_haltDisabledEvents($event)',
},
inputs: ['disabled'],
inputs: ['disabled', 'color'],
templateUrl: 'button.html',
styleUrls: ['button.css'],
encapsulation: ViewEncapsulation.None
})
export class MdAnchor extends MdButton {
constructor(
elementRef: ElementRef,
renderer: Renderer2,
platform: Platform,
focusOriginMonitor: FocusOriginMonitor) {
super(elementRef, renderer, platform, focusOriginMonitor);
focusOriginMonitor: FocusOriginMonitor,
elementRef: ElementRef,
renderer: Renderer2) {
super(renderer, elementRef, platform, focusOriginMonitor);
}
/** @docs-private */
+10 -31
View File
@@ -17,6 +17,7 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {FocusOrigin, FocusOriginMonitor, MdRipple, RippleRef} from '../core';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
/** Monotonically increasing integer used to auto-generate unique ids for checkbox components. */
@@ -57,8 +58,10 @@ export class MdCheckboxChange {
}
// Boilerplate for applying mixins to MdCheckbox.
export class MdCheckboxBase { }
export const _MdCheckboxMixinBase = mixinDisabled(MdCheckboxBase);
export class MdCheckboxBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdCheckboxMixinBase = mixinColor(mixinDisabled(MdCheckboxBase), 'accent');
/**
@@ -82,12 +85,12 @@ export const _MdCheckboxMixinBase = mixinDisabled(MdCheckboxBase);
'[class.mat-checkbox-label-before]': 'labelPosition == "before"',
},
providers: [MD_CHECKBOX_CONTROL_VALUE_ACCESSOR],
inputs: ['disabled'],
inputs: ['disabled', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdCheckbox extends _MdCheckboxMixinBase
implements ControlValueAccessor, AfterViewInit, OnDestroy, CanDisable {
implements ControlValueAccessor, AfterViewInit, OnDestroy, CanColor, CanDisable {
/**
* Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
* take precedence so this may be omitted.
@@ -183,19 +186,16 @@ export class MdCheckbox extends _MdCheckboxMixinBase
private _indeterminate: boolean = false;
private _color: string;
private _controlValueAccessorChangeFn: (value: any) => void = (value) => {};
/** Reference to the focused state ripple. */
private _focusRipple: RippleRef;
constructor(private _renderer: Renderer2,
private _elementRef: ElementRef,
constructor(renderer: Renderer2,
elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
private _focusOriginMonitor: FocusOriginMonitor) {
super();
this.color = 'accent';
super(renderer, elementRef);
}
ngAfterViewInit() {
@@ -247,27 +247,6 @@ export class MdCheckbox extends _MdCheckboxMixinBase
}
}
/** The color of the button. Can be `primary`, `accent`, or `warn`. */
@Input()
get color(): string { return this._color; }
set color(value: string) { this._updateColor(value); }
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
_setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
if (isAdd) {
this._renderer.addClass(this._elementRef.nativeElement, `mat-${color}`);
} else {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${color}`);
}
}
}
_isRippleDisabled() {
return this.disableRipple || this.disabled;
}
+13 -33
View File
@@ -11,11 +11,19 @@ import {
import {Focusable} from '../core/a11y/focus-key-manager';
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
export interface MdChipEvent {
chip: MdChip;
}
// Boilerplate for applying mixins to MdChip.
export class MdChipBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdChipMixinBase = mixinColor(MdChipBase, 'primary');
/**
* Material design styled Chip component. Used inside the MdChipList component.
*/
@@ -23,6 +31,7 @@ export interface MdChipEvent {
selector: `md-basic-chip, [md-basic-chip], md-chip, [md-chip],
mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]`,
template: `<ng-content></ng-content>`,
inputs: ['color'],
host: {
'[class.mat-chip]': 'true',
'tabindex': '-1',
@@ -35,7 +44,7 @@ export interface MdChipEvent {
'(click)': '_handleClick($event)'
}
})
export class MdChip implements Focusable, OnInit, OnDestroy {
export class MdChip extends _MdChipMixinBase implements Focusable, OnInit, OnDestroy, CanColor {
/** Whether or not the chip is disabled. Disabled chips cannot be focused. */
protected _disabled: boolean = null;
@@ -43,9 +52,6 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
/** Whether or not the chip is selected. */
protected _selected: boolean = false;
/** The palette color of selected chips. */
protected _color: string = 'primary';
/** Emitted when the chip is focused. */
onFocus = new EventEmitter<MdChipEvent>();
@@ -58,11 +64,12 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
/** Emitted when the chip is destroyed. */
@Output() destroy = new EventEmitter<MdChipEvent>();
constructor(protected _renderer: Renderer2, protected _elementRef: ElementRef) { }
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef);
}
ngOnInit(): void {
this._addDefaultCSSClass();
this._updateColor(this._color);
}
ngOnDestroy(): void {
@@ -108,15 +115,6 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
return this.selected;
}
/** The color of the chip. Can be `primary`, `accent`, or `warn`. */
@Input() get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
}
/** Allows for programmatic focusing of the chip. */
focus(): void {
this._elementRef.nativeElement.focus();
@@ -147,22 +145,4 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
this._renderer.addClass(el, 'mat-basic-chip');
}
}
/** Updates the private _color variable and the native element. */
private _updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
/** Sets the mat-color on the native element. */
private _setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
if (isAdd) {
this._renderer.addClass(this._elementRef.nativeElement, `mat-${color}`);
} else {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${color}`);
}
}
}
}
@@ -0,0 +1,84 @@
import {mixinColor} from './color';
import {ElementRef, Renderer2} from '@angular/core';
describe('MixinColor', () => {
it('should augment an existing class with a color property', () => {
const classWithColor = mixinColor(TestClass);
const instance = new classWithColor();
expect(instance.color)
.toBeFalsy('Expected the mixed-into class to have a color property');
instance.color = 'accent';
expect(instance.color)
.toBe('accent', 'Expected the mixed-into class to have an updated color property');
});
it('should remove old color classes if new color is set', () => {
const classWithColor = mixinColor(TestClass);
const instance = new classWithColor();
expect(instance.testElement.classList.length)
.toBe(0, 'Expected the element to not have any classes at initialization');
instance.color = 'primary';
expect(instance.testElement.classList)
.toContain('mat-primary', 'Expected the element to have the "mat-primary" class set');
instance.color = 'accent';
expect(instance.testElement.classList)
.not.toContain('mat-primary', 'Expected the element to no longer have "mat-primary" set.');
expect(instance.testElement.classList)
.toContain('mat-accent', 'Expected the element to have the "mat-accent" class set');
});
it('should allow having no color set', () => {
const classWithColor = mixinColor(TestClass);
const instance = new classWithColor();
expect(instance.testElement.classList.length)
.toBe(0, 'Expected the element to not have any classes at initialization');
instance.color = 'primary';
expect(instance.testElement.classList)
.toContain('mat-primary', 'Expected the element to have the "mat-primary" class set');
instance.color = null;
expect(instance.testElement.classList.length)
.toBe(0, 'Expected the element to have no color class set.');
});
it('should allow having a default color if specified', () => {
const classWithColor = mixinColor(TestClass, 'accent');
const instance = new classWithColor();
expect(instance.testElement.classList)
.toContain('mat-accent', 'Expected the element to have the "mat-accent" class by default.');
instance.color = null;
expect(instance.testElement.classList)
.toContain('mat-accent', 'Expected the default color "mat-accent" to be set.');
});
});
class TestClass {
testElement: HTMLElement = document.createElement('div');
/** Mock of a RendererV2 for the color mixin. */
_renderer: Renderer2 = {
addClass: (element: HTMLElement, className: string) => element.classList.add(className),
removeClass: (element: HTMLElement, className: string) => element.classList.remove(className)
} as any;
/** Fake instance of an ElementRef. */
_elementRef = new ElementRef(this.testElement);
}
+48
View File
@@ -0,0 +1,48 @@
import {Constructor} from './constructor';
import {ElementRef, Renderer2} from '@angular/core';
/** @docs-private */
export interface CanColor {
color: string;
}
/** @docs-private */
export interface HasRenderer {
_renderer: Renderer2;
_elementRef: ElementRef;
}
/** Possible color palette values. */
export type ThemePalette = 'primary' | 'accent' | 'warn' | null;
/** Mixin to augment a directive with a `color` property. */
export function mixinColor<T extends Constructor<HasRenderer>>(base: T, defaultColor?: ThemePalette)
: Constructor<CanColor> & T {
return class extends base {
private _color: ThemePalette = null;
get color(): ThemePalette { return this._color; }
set color(value: ThemePalette) {
const colorPalette = value || defaultColor;
if (colorPalette !== this._color) {
if (this._color) {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
}
if (colorPalette) {
this._renderer.addClass(this._elementRef.nativeElement, `mat-${colorPalette}`);
}
this._color = colorPalette;
}
}
constructor(...args: any[]) {
super(...args);
// Set the default color that can be specified from the mixin.
this.color = defaultColor;
}
};
}
@@ -0,0 +1,2 @@
/** @docs-private */
export type Constructor<T> = new(...args: any[]) => T;
+1 -4
View File
@@ -1,8 +1,5 @@
import {coerceBooleanProperty} from '../coercion/boolean-property';
/** @docs-private */
export type Constructor<T> = new(...args: any[]) => T;
import {Constructor} from './constructor';
/** @docs-private */
export interface CanDisable {
@@ -5,9 +5,18 @@ import {
ElementRef,
Renderer2,
} from '@angular/core';
import {CanColor, mixinColor} from '../../common-behaviors/color';
export type MdPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate';
// Boilerplate for applying mixins to MdChip.
export class MdPseudoCheckboxBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdPseudoCheckboxBase = mixinColor(MdPseudoCheckboxBase, 'accent');
/**
* Component that shows a simplified checkbox without including any kind of "real" checkbox.
* Meant to be used when the checkbox is purely decorative and a large number of them will be
@@ -24,6 +33,7 @@ export type MdPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate';
encapsulation: ViewEncapsulation.None,
selector: 'md-pseudo-checkbox, mat-pseudo-checkbox',
styleUrls: ['pseudo-checkbox.css'],
inputs: ['color'],
template: '',
host: {
'[class.mat-pseudo-checkbox]': 'true',
@@ -32,29 +42,14 @@ export type MdPseudoCheckboxState = 'unchecked' | 'checked' | 'indeterminate';
'[class.mat-pseudo-checkbox-disabled]': 'disabled',
},
})
export class MdPseudoCheckbox {
export class MdPseudoCheckbox extends _MdPseudoCheckboxBase implements CanColor {
/** Display state of the checkbox. */
@Input() state: MdPseudoCheckboxState = 'unchecked';
/** Whether the checkbox is disabled. */
@Input() disabled: boolean = false;
/** Color of the checkbox. */
@Input()
get color(): string { return this._color; }
set color(value: string) {
if (value) {
let nativeElement = this._elementRef.nativeElement;
this._renderer.removeClass(nativeElement, `mat-${this.color}`);
this._renderer.addClass(nativeElement, `mat-${value}`);
this._color = value;
}
}
private _color: string;
constructor(private _elementRef: ElementRef, private _renderer: Renderer2) {
this.color = 'accent';
constructor(elementRef: ElementRef, renderer: Renderer2) {
super(renderer, elementRef);
}
}
+15 -26
View File
@@ -11,6 +11,14 @@ import {
AfterViewChecked,
} from '@angular/core';
import {MdIconRegistry} from './icon-registry';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
// Boilerplate for applying mixins to MdIcon.
export class MdIconBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdIconMixinBase = mixinColor(MdIconBase);
/**
@@ -51,6 +59,7 @@ import {MdIconRegistry} from './icon-registry';
template: '<ng-content></ng-content>',
selector: 'md-icon, mat-icon',
styleUrls: ['icon.css'],
inputs: ['color'],
host: {
'role': 'img',
'[class.mat-icon]': 'true',
@@ -58,8 +67,8 @@ import {MdIconRegistry} from './icon-registry';
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdIcon implements OnChanges, OnInit, AfterViewChecked {
private _color: string;
export class MdIcon extends _MdIconMixinBase implements OnChanges, OnInit, AfterViewChecked,
CanColor {
/** Name of the icon in the SVG icon set. */
@Input() svgIcon: string;
@@ -76,34 +85,14 @@ export class MdIcon implements OnChanges, OnInit, AfterViewChecked {
/** Screenreader label for the icon. */
@Input('aria-label') hostAriaLabel: string = '';
/** Color of the icon. */
@Input()
get color(): string { return this._color; }
set color(value: string) { this._updateColor(value); }
private _previousFontSetClass: string;
private _previousFontIconClass: string;
private _previousAriaLabel: string;
constructor(
private _elementRef: ElementRef,
private _renderer: Renderer2,
private _mdIconRegistry: MdIconRegistry) { }
_updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
_setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
if (isAdd) {
this._renderer.addClass(this._elementRef.nativeElement, `mat-${color}`);
} else {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${color}`);
}
}
constructor(private _mdIconRegistry: MdIconRegistry,
renderer: Renderer2,
elementRef: ElementRef) {
super(renderer, elementRef);
}
/**
+17 -18
View File
@@ -10,6 +10,7 @@ import {
Directive,
ViewChild,
} from '@angular/core';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
// TODO(josephperrott): Benchpress tests.
@@ -49,6 +50,11 @@ type EasingFn = (currentTime: number, startValue: number,
})
export class MdProgressSpinnerCssMatStyler {}
// Boilerplate for applying mixins to MdProgressSpinner.
export class MdProgressSpinnerBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdProgressSpinnerMixinBase = mixinColor(MdProgressSpinnerBase, 'primary');
/**
* <md-progress-spinner> component.
@@ -61,11 +67,14 @@ export class MdProgressSpinnerCssMatStyler {}
'[attr.aria-valuemin]': '_ariaValueMin',
'[attr.aria-valuemax]': '_ariaValueMax'
},
inputs: ['color'],
templateUrl: 'progress-spinner.html',
styleUrls: ['progress-spinner.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MdProgressSpinner implements OnDestroy {
export class MdProgressSpinner extends _MdProgressSpinnerMixinBase
implements OnDestroy, CanColor {
/** The id of the last requested animation. */
private _lastAnimationId: number = 0;
@@ -77,7 +86,6 @@ export class MdProgressSpinner implements OnDestroy {
private _mode: ProgressSpinnerMode = 'determinate';
private _value: number;
private _color: string = 'primary';
/** Stroke width of the progress spinner. By default uses 10px as stroke width. */
@Input() strokeWidth: number = PROGRESS_SPINNER_STROKE_WIDTH;
@@ -112,17 +120,6 @@ export class MdProgressSpinner implements OnDestroy {
this._cleanupIndeterminateAnimation();
}
/** The color of the progress-spinner. Can be primary, accent, or warn. */
@Input()
get color(): string { return this._color; }
set color(value: string) {
if (value) {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${this._color}`);
this._renderer.addClass(this._elementRef.nativeElement, `mat-${value}`);
this._color = value;
}
}
/** Value of the progress circle. It is bound to the host as the attribute aria-valuenow. */
@Input()
@HostBinding('attr.aria-valuenow')
@@ -162,10 +159,11 @@ export class MdProgressSpinner implements OnDestroy {
}
}
constructor(
private _ngZone: NgZone,
private _elementRef: ElementRef,
private _renderer: Renderer2) { }
constructor(renderer: Renderer2,
elementRef: ElementRef,
private _ngZone: NgZone) {
super(renderer, elementRef);
}
/**
@@ -274,13 +272,14 @@ export class MdProgressSpinner implements OnDestroy {
'mode': 'indeterminate',
'[class.mat-spinner]': 'true',
},
inputs: ['color'],
templateUrl: 'progress-spinner.html',
styleUrls: ['progress-spinner.css'],
})
export class MdSpinner extends MdProgressSpinner implements OnDestroy {
constructor(elementRef: ElementRef, ngZone: NgZone, renderer: Renderer2) {
super(ngZone, elementRef, renderer);
super(renderer, elementRef, ngZone);
this.mode = 'indeterminate';
}
+20 -20
View File
@@ -34,6 +34,8 @@ import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/filter';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisable} from '../core/common-behaviors/disabled';
/**
@@ -95,11 +97,19 @@ export class MdSelectChange {
/** Allowed values for the floatPlaceholder option. */
export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
// Boilerplate for applying mixins to MdSelect.
export class MdSelectBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdSelectMixinBase = mixinColor(MdSelectBase, 'primary');
@Component({
moduleId: module.id,
selector: 'md-select, mat-select',
templateUrl: 'select.html',
styleUrls: ['select.css'],
inputs: ['color'],
encapsulation: ViewEncapsulation.None,
host: {
'role': 'listbox',
@@ -122,7 +132,8 @@ export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
],
exportAs: 'mdSelect',
})
export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlValueAccessor {
export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, OnDestroy, OnInit,
ControlValueAccessor, CanColor {
/** Whether or not the overlay panel is open. */
private _panelOpen = false;
@@ -159,9 +170,6 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
/** Tab index for the element. */
private _tabIndex: number;
/** Theme color for the component. */
private _color: string;
/**
* The width of the trigger. Must be saved to set the min width of the overlay panel
* and the width of the selected value.
@@ -288,17 +296,6 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
/** Input that can be used to specify the `aria-labelledby` attribute. */
@Input('aria-labelledby') ariaLabelledby: string = '';
/** Theme color for the component. */
@Input()
get color(): string { return this._color; }
set color(value: string) {
if (value && value !== this._color) {
this._renderer.removeClass(this._element.nativeElement, `mat-${this._color}`);
this._renderer.addClass(this._element.nativeElement, `mat-${value}`);
this._color = value;
}
}
/** Combined stream of all of the child options' change events. */
get optionSelectionChanges(): Observable<MdOptionSelectionChange> {
return Observable.merge(...this.options.map(option => option.onSelectionChange));
@@ -313,10 +310,14 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
/** Event emitted when the selected value has been changed by the user. */
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
constructor(private _element: ElementRef, private _renderer: Renderer2,
private _viewportRuler: ViewportRuler, private _changeDetectorRef: ChangeDetectorRef,
@Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl,
constructor(private _viewportRuler: ViewportRuler,
private _changeDetectorRef: ChangeDetectorRef,
renderer: Renderer2,
elementRef: ElementRef,
@Optional() private _dir: Dir,
@Self() @Optional() public _control: NgControl,
@Attribute('tabindex') tabIndex: string) {
super(renderer, elementRef);
if (this._control) {
this._control.valueAccessor = this;
@@ -327,7 +328,6 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
ngOnInit() {
this._selectionModel = new SelectionModel<MdOption>(this.multiple, null, false);
this.color = this.color || 'primary';
}
ngAfterContentInit() {
@@ -742,7 +742,7 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
/** Focuses the host element when the panel closes. */
private _focusHost(): void {
this._element.nativeElement.focus();
this._elementRef.nativeElement.focus();
}
/** Gets the index of the provided option in the option list. */
+10 -31
View File
@@ -24,6 +24,7 @@ import {
} from '../core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
export const MD_SLIDE_TOGGLE_VALUE_ACCESSOR: any = {
@@ -44,8 +45,10 @@ let nextId = 0;
// Boilerplate for applying mixins to MdSlideToggle.
export class MdSlideToggleBase { }
export const _MdSlideToggleMixinBase = mixinDisabled(MdSlideToggleBase);
export class MdSlideToggleBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdSlideToggleMixinBase = mixinColor(mixinDisabled(MdSlideToggleBase), 'accent');
/** Represents a slidable "switch" toggle that can be moved between on and off. */
@Component({
@@ -60,19 +63,18 @@ export const _MdSlideToggleMixinBase = mixinDisabled(MdSlideToggleBase);
templateUrl: 'slide-toggle.html',
styleUrls: ['slide-toggle.css'],
providers: [MD_SLIDE_TOGGLE_VALUE_ACCESSOR],
inputs: ['disabled'],
inputs: ['disabled', 'color'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MdSlideToggle extends _MdSlideToggleMixinBase
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable {
implements OnDestroy, AfterContentInit, ControlValueAccessor, CanDisable, CanColor {
private onChange = (_: any) => {};
private onTouched = () => {};
// A unique id for the slide-toggle. By default the id is auto-generated.
private _uniqueId = `md-slide-toggle-${++nextId}`;
private _checked: boolean = false;
private _color: string;
private _slideRenderer: SlideToggleRenderer = null;
private _required: boolean = false;
private _disableRipple: boolean = false;
@@ -120,11 +122,11 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
/** Reference to the ripple directive on the thumb container. */
@ViewChild(MdRipple) _ripple: MdRipple;
constructor(private _elementRef: ElementRef,
private _renderer: Renderer2,
constructor(elementRef: ElementRef,
renderer: Renderer2,
private _focusOriginMonitor: FocusOriginMonitor,
private _changeDetectorRef: ChangeDetectorRef) {
super();
super(renderer, elementRef);
}
ngAfterContentInit() {
@@ -210,13 +212,6 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
}
}
/** The color of the slide-toggle. Can be primary, accent, or warn. */
@Input()
get color(): string { return this._color; }
set color(value: string) {
this._updateColor(value);
}
/** Toggles the checked state of the slide-toggle. */
toggle() {
this.checked = !this.checked;
@@ -238,22 +233,6 @@ export class MdSlideToggle extends _MdSlideToggleMixinBase
}
}
private _updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
private _setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
if (isAdd) {
this._renderer.addClass(this._elementRef.nativeElement, `mat-${color}`);
} else {
this._renderer.removeClass(this._elementRef.nativeElement, `mat-${color}`);
}
}
}
/** Emits the change event to the `change` output EventEmitter */
private _emitChangeEvent() {
let event = new MdSlideToggleChange();
+12 -32
View File
@@ -1,12 +1,12 @@
import {
Component,
ChangeDetectionStrategy,
Input,
ViewEncapsulation,
Directive,
ElementRef,
Renderer2,
} from '@angular/core';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
@Directive({
@@ -17,11 +17,19 @@ import {
})
export class MdToolbarRow {}
// Boilerplate for applying mixins to MdToolbar.
export class MdToolbarBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdToolbarMixinBase = mixinColor(MdToolbarBase);
@Component({
moduleId: module.id,
selector: 'md-toolbar, mat-toolbar',
templateUrl: 'toolbar.html',
styleUrls: ['toolbar.css'],
inputs: ['color'],
host: {
'[class.mat-toolbar]': 'true',
'role': 'toolbar'
@@ -29,38 +37,10 @@ export class MdToolbarRow {}
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class MdToolbar {
export class MdToolbar extends _MdToolbarMixinBase implements CanColor {
private _color: string;
constructor(private _elementRef: ElementRef, private _renderer: Renderer2) { }
/** The color of the toolbar. Can be primary, accent, or warn. */
@Input()
get color(): string {
return this._color;
}
set color(value: string) {
this._updateColor(value);
}
private _updateColor(newColor: string) {
this._setElementColor(this._color, false);
this._setElementColor(newColor, true);
this._color = newColor;
}
private _setElementColor(color: string, isAdd: boolean) {
if (color != null && color != '') {
let element = this._elementRef.nativeElement;
if (isAdd) {
this._renderer.addClass(element, `mat-${color}`);
} else {
this._renderer.removeClass(element, `mat-${color}`);
}
}
constructor(renderer: Renderer2, elementRef: ElementRef) {
super(renderer, elementRef);
}
}