mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
committed by
Kara
parent
94a2855d45
commit
3a29d671d2
@@ -4,7 +4,7 @@
|
||||
<md-card>
|
||||
<md-card-subtitle>ngModel</md-card-subtitle>
|
||||
|
||||
<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="drinksRequired" [disabled]="drinksDisabled"
|
||||
<md-select placeholder="Drink" [color]="drinksTheme" [(ngModel)]="currentDrink" [required]="drinksRequired" [disabled]="drinksDisabled"
|
||||
[floatPlaceholder]="floatPlaceholder" #drinkControl="ngModel">
|
||||
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
|
||||
{{ drink.viewValue }}
|
||||
@@ -22,6 +22,12 @@
|
||||
<option value="never">Never</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="drinks-theme">Theme:</label>
|
||||
<select [(ngModel)]="drinksTheme" id="drinks-theme">
|
||||
<option *ngFor="let theme of availableThemes" [value]="theme.value">{{ theme.name }}</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<button md-button (click)="currentDrink='water-2'">SET VALUE</button>
|
||||
<button md-button (click)="drinksRequired=!drinksRequired">TOGGLE REQUIRED</button>
|
||||
@@ -33,7 +39,7 @@
|
||||
<md-card-subtitle>Multiple selection</md-card-subtitle>
|
||||
|
||||
<md-card-content>
|
||||
<md-select multiple placeholder="Pokemon" [(ngModel)]="currentPokemon"
|
||||
<md-select multiple [color]="pokemonTheme" placeholder="Pokemon" [(ngModel)]="currentPokemon"
|
||||
[required]="pokemonRequired" [disabled]="pokemonDisabled" #pokemonControl="ngModel">
|
||||
<md-option *ngFor="let creature of pokemon" [value]="creature.value">
|
||||
{{ creature.viewValue }}
|
||||
@@ -43,6 +49,12 @@
|
||||
<p> Touched: {{ pokemonControl.touched }} </p>
|
||||
<p> Dirty: {{ pokemonControl.dirty }} </p>
|
||||
<p> Status: {{ pokemonControl.control?.status }} </p>
|
||||
<p>
|
||||
<label for="pokemon-theme">Theme:</label>
|
||||
<select [(ngModel)]="pokemonTheme" id="pokemon-theme">
|
||||
<option *ngFor="let theme of availableThemes" [value]="theme.value">{{ theme.name }}</option>
|
||||
</select>
|
||||
</p>
|
||||
<button md-button (click)="setPokemonValue()">SET VALUE</button>
|
||||
<button md-button (click)="pokemonRequired=!pokemonRequired">TOGGLE REQUIRED</button>
|
||||
<button md-button (click)="pokemonDisabled=!pokemonDisabled">TOGGLE DISABLED</button>
|
||||
|
||||
@@ -19,6 +19,8 @@ export class SelectDemo {
|
||||
latestChangeEvent: MdSelectChange;
|
||||
floatPlaceholder: string = 'auto';
|
||||
foodControl = new FormControl('pizza-1');
|
||||
drinksTheme = 'primary';
|
||||
pokemonTheme = 'primary';
|
||||
|
||||
foods = [
|
||||
{value: 'steak-0', viewValue: 'Steak'},
|
||||
@@ -48,6 +50,12 @@ export class SelectDemo {
|
||||
{value: 'psyduck-6', viewValue: 'Psyduck'},
|
||||
];
|
||||
|
||||
availableThemes = [
|
||||
{value: 'primary', name: 'Primary' },
|
||||
{value: 'accent', name: 'Accent' },
|
||||
{value: 'warn', name: 'Warn' }
|
||||
];
|
||||
|
||||
toggleDisabled() {
|
||||
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
|
||||
}
|
||||
|
||||
@@ -5,19 +5,29 @@
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
|
||||
.mat-option {
|
||||
&:hover:not(.mat-option-disabled), &:focus:not(.mat-option-disabled) {
|
||||
background: mat-color($background, hover);
|
||||
}
|
||||
|
||||
&.mat-selected {
|
||||
&.mat-selected.mat-primary, .mat-primary &.mat-selected {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
// In multiple mode there is a checkbox to show that the option is selected.
|
||||
&:not(.mat-option-multiple) {
|
||||
background: mat-color($background, hover);
|
||||
}
|
||||
&.mat-selected.mat-accent, .mat-accent &.mat-selected {
|
||||
color: mat-color($accent);
|
||||
}
|
||||
|
||||
&.mat-selected.mat-warn, .mat-warn &.mat-selected {
|
||||
color: mat-color($warn);
|
||||
}
|
||||
|
||||
// In multiple mode there is a checkbox to show that the option is selected.
|
||||
&.mat-selected:not(.mat-option-multiple) {
|
||||
background: mat-color($background, hover);
|
||||
}
|
||||
|
||||
&.mat-active {
|
||||
@@ -28,6 +38,5 @@
|
||||
&.mat-option-disabled {
|
||||
color: mat-color($foreground, hint-text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
@import '../../theming/theming';
|
||||
|
||||
@mixin _mat-pseudo-checkbox-inner-content-theme($theme, $pallete-name) {
|
||||
$pallete: map-get($theme, $pallete-name);
|
||||
$color: mat-color($pallete, 500);
|
||||
|
||||
.mat-pseudo-checkbox-checked.mat-#{$pallete-name},
|
||||
.mat-pseudo-checkbox-indeterminate.mat-#{$pallete-name},
|
||||
.mat-#{$pallete-name} .mat-pseudo-checkbox-checked,
|
||||
.mat-#{$pallete-name} .mat-pseudo-checkbox-indeterminate {
|
||||
background: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mat-pseudo-checkbox-theme($theme) {
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
@@ -14,6 +25,7 @@
|
||||
$white-30pct-opacity-on-dark: #686868;
|
||||
$black-26pct-opacity-on-light: #b0b0b0;
|
||||
$disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);
|
||||
$colored-box-selector: '.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate';
|
||||
|
||||
.mat-pseudo-checkbox {
|
||||
color: mat-color(map-get($theme, foreground), secondary-text);
|
||||
@@ -23,19 +35,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include _mat-pseudo-checkbox-inner-content-theme($theme, primary);
|
||||
@include _mat-pseudo-checkbox-inner-content-theme($theme, accent);
|
||||
@include _mat-pseudo-checkbox-inner-content-theme($theme, warn);
|
||||
|
||||
.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate {
|
||||
&.mat-primary {
|
||||
background: mat-color($primary, 500);
|
||||
}
|
||||
|
||||
&.mat-accent {
|
||||
background: mat-color($accent, 500);
|
||||
}
|
||||
|
||||
&.mat-warn {
|
||||
background: mat-color($warn, 500);
|
||||
}
|
||||
|
||||
&.mat-pseudo-checkbox-disabled {
|
||||
background: $disabled-color;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,33 @@
|
||||
@import '../core/theming/palette';
|
||||
@import '../core/theming/theming';
|
||||
|
||||
@mixin _mat-select-inner-content-theme($palette) {
|
||||
$color: mat-color($palette);
|
||||
|
||||
.mat-select-trigger, .mat-select-arrow {
|
||||
color: $color;
|
||||
}
|
||||
|
||||
.mat-select-underline {
|
||||
background-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin mat-select-theme($theme) {
|
||||
$foreground: map-get($theme, foreground);
|
||||
$background: map-get($theme, background);
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
|
||||
.mat-select-trigger {
|
||||
color: mat-color($foreground, hint-text);
|
||||
|
||||
.mat-select:focus:not(.mat-select-disabled) & {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
|
||||
color: mat-color($warn);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-select-underline {
|
||||
background-color: mat-color($foreground, divider);
|
||||
|
||||
.mat-select:focus:not(.mat-select-disabled) & {
|
||||
background-color: mat-color($primary);
|
||||
}
|
||||
|
||||
.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
|
||||
background-color: mat-color($warn);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-select-arrow {
|
||||
.mat-select-disabled .mat-select-value,
|
||||
.mat-select-arrow,
|
||||
.mat-select-trigger {
|
||||
color: mat-color($foreground, hint-text);
|
||||
|
||||
.mat-select:focus:not(.mat-select-disabled) & {
|
||||
color: mat-color($primary);
|
||||
}
|
||||
|
||||
.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
|
||||
color: mat-color($warn);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-select-content, .mat-select-panel-done-animating {
|
||||
@@ -49,9 +36,20 @@
|
||||
|
||||
.mat-select-value {
|
||||
color: mat-color($foreground, text);
|
||||
}
|
||||
|
||||
.mat-select-disabled & {
|
||||
color: mat-color($foreground, hint-text);
|
||||
.mat-select:focus:not(.mat-select-disabled) {
|
||||
&.mat-primary {
|
||||
@include _mat-select-inner-content-theme($primary);
|
||||
}
|
||||
|
||||
&.mat-accent {
|
||||
@include _mat-select-inner-content-theme($accent);
|
||||
}
|
||||
}
|
||||
|
||||
.mat-select:focus:not(.mat-select-disabled).mat-warn,
|
||||
.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) {
|
||||
@include _mat-select-inner-content-theme($warn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
[offsetY]="_offsetY" [offsetX]="_offsetX" (attach)="_setScrollTop()" (detach)="close()">
|
||||
<div class="mat-select-panel" [@transformPanel]="'showing'" (@transformPanel.done)="_onPanelDone()"
|
||||
(keydown)="_keyManager.onKeydown($event)" [style.transformOrigin]="_transformOrigin"
|
||||
[class.mat-select-panel-done-animating]="_panelDoneAnimating">
|
||||
[class.mat-select-panel-done-animating]="_panelDoneAnimating" [ngClass]="'mat-' + color">
|
||||
<div class="mat-select-content" [@fadeInContent]="'showing'" (@fadeInContent.done)="_onFadeInDone()">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -50,7 +50,8 @@ describe('MdSelect', () => {
|
||||
SelectWithPlainTabindex,
|
||||
SelectEarlyAccessSibling,
|
||||
BasicSelectInitiallyHidden,
|
||||
BasicSelectNoPlaceholder
|
||||
BasicSelectNoPlaceholder,
|
||||
BasicSelectWithTheming
|
||||
],
|
||||
providers: [
|
||||
{provide: OverlayContainer, useFactory: () => {
|
||||
@@ -1709,6 +1710,55 @@ describe('MdSelect', () => {
|
||||
|
||||
});
|
||||
|
||||
describe('theming', () => {
|
||||
let fixture: ComponentFixture<BasicSelectWithTheming>;
|
||||
let testInstance: BasicSelectWithTheming;
|
||||
let selectElement: HTMLElement;
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(BasicSelectWithTheming);
|
||||
testInstance = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
selectElement = fixture.debugElement.query(By.css('.mat-select')).nativeElement;
|
||||
}));
|
||||
|
||||
it('should default to the primary theme', () => {
|
||||
expect(fixture.componentInstance.select.color).toBe('primary');
|
||||
expect(selectElement.classList).toContain('mat-primary');
|
||||
});
|
||||
|
||||
it('should be able to override the theme', () => {
|
||||
fixture.componentInstance.theme = 'accent';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.select.color).toBe('accent');
|
||||
expect(selectElement.classList).toContain('mat-accent');
|
||||
expect(selectElement.classList).not.toContain('mat-primary');
|
||||
});
|
||||
|
||||
it('should not be able to set a blank theme', () => {
|
||||
fixture.componentInstance.theme = '';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.select.color).toBe('primary');
|
||||
expect(selectElement.classList).toContain('mat-primary');
|
||||
});
|
||||
|
||||
it('should pass the theme to the panel', () => {
|
||||
fixture.componentInstance.theme = 'warn';
|
||||
fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
const panel = overlayContainerElement.querySelector('.mat-select-panel');
|
||||
|
||||
expect(fixture.componentInstance.select.color).toBe('warn');
|
||||
expect(selectElement.classList).toContain('mat-warn');
|
||||
expect(panel.classList).toContain('mat-warn');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -2040,6 +2090,20 @@ class BasicSelectInitiallyHidden {
|
||||
})
|
||||
class BasicSelectNoPlaceholder { }
|
||||
|
||||
@Component({
|
||||
selector: 'basic-select-with-theming',
|
||||
template: `
|
||||
<md-select placeholder="Food" [color]="theme">
|
||||
<md-option value="steak-0">Steak</md-option>
|
||||
<md-option value="pizza-1">Pizza</md-option>
|
||||
</md-select>
|
||||
`
|
||||
})
|
||||
class BasicSelectWithTheming {
|
||||
@ViewChild(MdSelect) select: MdSelect;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
class FakeViewportRuler {
|
||||
getViewportRect() {
|
||||
return {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Optional,
|
||||
Output,
|
||||
QueryList,
|
||||
Renderer,
|
||||
Renderer2,
|
||||
Self,
|
||||
ViewEncapsulation,
|
||||
ViewChild,
|
||||
@@ -111,7 +111,7 @@ export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
|
||||
'[class.mat-select-disabled]': 'disabled',
|
||||
'[class.mat-select]': 'true',
|
||||
'(keydown)': '_handleKeydown($event)',
|
||||
'(blur)': '_onBlur()'
|
||||
'(blur)': '_onBlur()',
|
||||
},
|
||||
animations: [
|
||||
transformPlaceholder,
|
||||
@@ -157,6 +157,9 @@ 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.
|
||||
@@ -287,6 +290,17 @@ 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));
|
||||
@@ -301,7 +315,7 @@ 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: Renderer,
|
||||
constructor(private _element: ElementRef, private _renderer: Renderer2,
|
||||
private _viewportRuler: ViewportRuler, private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Optional() private _dir: Dir, @Self() @Optional() public _control: NgControl,
|
||||
@Attribute('tabindex') tabIndex: string) {
|
||||
@@ -314,6 +328,7 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
|
||||
|
||||
ngOnInit() {
|
||||
this._selectionModel = new SelectionModel<MdOption>(this.multiple, null, false);
|
||||
this.color = this.color || 'primary';
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
@@ -686,7 +701,7 @@ export class MdSelect implements AfterContentInit, OnDestroy, OnInit, ControlVal
|
||||
|
||||
/** Focuses the host element when the panel closes. */
|
||||
private _focusHost(): void {
|
||||
this._renderer.invokeElementMethod(this._element.nativeElement, 'focus');
|
||||
this._element.nativeElement.focus();
|
||||
}
|
||||
|
||||
/** Gets the index of the provided option in the option list. */
|
||||
|
||||
Reference in New Issue
Block a user