mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(chips): add (select), [color] and dark theme to chips. (#2242)
Add new functionality/options to chips for managing selection/color. *MdChipList Options* - `[selectable]` - Programmatically control whether or not the chips in the list are capable of being selected. - The SPACE key will automatically select the currently focused chip. *MdChip Options* - `[color]` - Programmatically control the selected color of the chip. - `[selected]` - Programmatically control whether or not the chip is selected. - `(select)` - Event emitted when the chip is selected. - `(deselect)` - Event emitted when the chip is deselected. Additionally, adds basic support for dark themeed chips using existing colors from other components in the spec and cleanup demos by using cards and toolbars like other demos. References #120.
This commit is contained in:
committed by
Jeremy Elbourn
parent
e4d2bef855
commit
f45c315883
@@ -1,68 +1,65 @@
|
||||
<div class="chips-demo">
|
||||
<section>
|
||||
<h3>Static Chips</h3>
|
||||
<md-card>
|
||||
<md-toolbar color="primary">Static Chips</md-toolbar>
|
||||
|
||||
<h5>Simple</h5>
|
||||
<md-card-content>
|
||||
<h4>Simple</h4>
|
||||
|
||||
<md-chip-list>
|
||||
<md-chip>Chip 1</md-chip>
|
||||
<md-chip>Chip 2</md-chip>
|
||||
<md-chip>Chip 3</md-chip>
|
||||
</md-chip-list>
|
||||
<md-chip-list>
|
||||
<md-chip>Chip 1</md-chip>
|
||||
<md-chip>Chip 2</md-chip>
|
||||
<md-chip>Chip 3</md-chip>
|
||||
</md-chip-list>
|
||||
|
||||
<h5>Advanced</h5>
|
||||
<h4>Unstyled</h4>
|
||||
|
||||
<md-chip-list>
|
||||
<md-chip class="md-accent selected">Selected/Colored</md-chip>
|
||||
<md-chip class="md-warn" *ngIf="visible"
|
||||
(destroy)="alert('chip destroyed')" (click)="toggleVisible()">
|
||||
With Events
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
<md-chip-list>
|
||||
<md-basic-chip>Basic Chip 1</md-basic-chip>
|
||||
<md-basic-chip>Basic Chip 2</md-basic-chip>
|
||||
<md-basic-chip>Basic Chip 3</md-basic-chip>
|
||||
</md-chip-list>
|
||||
|
||||
<h5>Unstyled</h5>
|
||||
<h4>Advanced</h4>
|
||||
|
||||
<md-chip-list>
|
||||
<md-basic-chip>Basic Chip 1</md-basic-chip>
|
||||
<md-basic-chip>Basic Chip 2</md-basic-chip>
|
||||
<md-basic-chip>Basic Chip 3</md-basic-chip>
|
||||
</md-chip-list>
|
||||
<md-chip-list selectable="false">
|
||||
<md-chip color="accent" selected="true">Selected/Colored</md-chip>
|
||||
<md-chip color="warn" selected="true" *ngIf="visible"
|
||||
(destroy)="alert('chip destroyed')" (click)="toggleVisible()">
|
||||
With Events
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
|
||||
<h3>Material Contributors</h3>
|
||||
<md-card>
|
||||
<md-toolbar color="primary">Dynamic Chips</md-toolbar>
|
||||
|
||||
<md-chip-list>
|
||||
<md-chip *ngFor="let person of people; let even = even" [ngClass]="[color, even ? 'selected' : '' ]">
|
||||
{{person.name}}
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
<md-card-content>
|
||||
<h4>Input Container</h4>
|
||||
|
||||
<br />
|
||||
<md-chip-list>
|
||||
<md-chip *ngFor="let person of people" [color]="color">
|
||||
{{person.name}}
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
|
||||
<md-input #input (keyup.enter)="add(input)" (blur)="add(input)" placeholder="New Contributor...">
|
||||
</md-input>
|
||||
<md-input-container>
|
||||
<input md-input #input (keyup.enter)="add(input)" placeholder="New Contributor..."/>
|
||||
</md-input-container>
|
||||
|
||||
<h3>Stacked Chips</h3>
|
||||
<h4>Stacked Chips</h4>
|
||||
|
||||
<p>
|
||||
You can also stack the chips if you want them on top of each other.
|
||||
</p>
|
||||
<p>
|
||||
You can also stack the chips if you want them on top of each other and/or use the
|
||||
<code>(focus)</code> event to run custom code.
|
||||
</p>
|
||||
|
||||
<md-chip-list class="md-chip-list-stacked">
|
||||
<md-chip (focus)="color = ''" class="selected">
|
||||
None
|
||||
</md-chip>
|
||||
|
||||
<md-chip (focus)="color = 'md-primary'" class="selected md-primary">
|
||||
Primary
|
||||
</md-chip>
|
||||
|
||||
<md-chip (focus)="color = 'md-accent'" class="selected md-accent">
|
||||
Accent
|
||||
</md-chip>
|
||||
|
||||
<md-chip (focus)="color = 'md-warn'" class="selected md-warn">
|
||||
Warn
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
</section>
|
||||
<md-chip-list class="md-chip-list-stacked">
|
||||
<md-chip *ngFor="let aColor of availableColors"
|
||||
(focus)="color = aColor.color" color="{{aColor.color}}" selected="true">
|
||||
{{aColor.name}}
|
||||
</md-chip>
|
||||
</md-chip-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</div>
|
||||
@@ -4,6 +4,19 @@
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
md-card {
|
||||
padding: 0;
|
||||
margin: 16px;
|
||||
|
||||
& md-toolbar {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& md-card-content {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
md-basic-chip {
|
||||
margin: auto 10px;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ export interface Person {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface DemoColor {
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
moduleId: module.id,
|
||||
selector: 'chips-demo',
|
||||
@@ -24,6 +29,13 @@ export class ChipsDemo {
|
||||
{ name: 'Paul' }
|
||||
];
|
||||
|
||||
availableColors: DemoColor[] = [
|
||||
{ name: 'none', color: '' },
|
||||
{ name: 'Primary', color: 'primary' },
|
||||
{ name: 'Accent', color: 'accent' },
|
||||
{ name: 'Warn', color: 'warn' }
|
||||
];
|
||||
|
||||
alert(message: string): void {
|
||||
alert(message);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
@import '../core/theming/palette';
|
||||
@import '../core/theming/theming';
|
||||
|
||||
@mixin md-chips-theme($theme) {
|
||||
@@ -6,27 +7,40 @@
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
// Use spec-recommended color for regular foreground, and utilise contrast color for a grey very
|
||||
// close to the selected spec since no guidance is provided and to ensure palette consistency.
|
||||
$light-foreground: rgba(0, 0, 0, 0.87);
|
||||
$light-selected-foreground: md-contrast($md-grey, 600);
|
||||
|
||||
// The spec only provides guidance for light-themed chips. When inside of a dark theme, fall back
|
||||
// to standard background and foreground colors.
|
||||
$unselected-background: if($is-dark-theme, md-color($background, card), #e0e0e0);
|
||||
$unselected-foreground: if($is-dark-theme, md-color($foreground, text), $light-foreground);
|
||||
|
||||
$selected-background: if($is-dark-theme, md-color($background, app-bar), #808080);
|
||||
$selected-foreground: if($is-dark-theme, md-color($foreground, text), $light-selected-foreground);
|
||||
|
||||
.md-chip {
|
||||
background-color: #e0e0e0;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
background-color: $unselected-background;
|
||||
color: $unselected-foreground;
|
||||
}
|
||||
|
||||
.md-chip.selected {
|
||||
// There is no dark theme in the current spec, so this applies to both
|
||||
background-color: #808080;
|
||||
|
||||
// Use a contrast color for a grey very close to the background color
|
||||
color: md-contrast($md-grey, 600);
|
||||
.md-chip.md-chip-selected {
|
||||
background-color: $selected-background;
|
||||
color: $selected-foreground;
|
||||
|
||||
&.md-primary {
|
||||
background-color: md-color($primary, 500);
|
||||
color: md-contrast($primary, 500);
|
||||
}
|
||||
|
||||
&.md-accent {
|
||||
background-color: md-color($accent, 500);
|
||||
color: md-contrast($accent, 500);
|
||||
}
|
||||
|
||||
&.md-warn {
|
||||
background-color: md-color($warn, 500);
|
||||
color: md-contrast($warn, 500);
|
||||
|
||||
@@ -3,6 +3,8 @@ import {Component, DebugElement, QueryList} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {MdChip, MdChipList, MdChipsModule} from './index';
|
||||
import {ListKeyManager} from '../core/a11y/list-key-manager';
|
||||
import {FakeEvent} from '../core/a11y/list-key-manager.spec';
|
||||
import {SPACE} from '../core/keyboard/keycodes';
|
||||
|
||||
describe('MdChipList', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
@@ -10,7 +12,7 @@ describe('MdChipList', () => {
|
||||
let chipListNativeElement: HTMLElement;
|
||||
let chipListInstance: MdChipList;
|
||||
let testComponent: StaticChipList;
|
||||
let items: QueryList<MdChip>;
|
||||
let chips: QueryList<MdChip>;
|
||||
let manager: ListKeyManager;
|
||||
|
||||
beforeEach(async(() => {
|
||||
@@ -22,9 +24,7 @@ describe('MdChipList', () => {
|
||||
});
|
||||
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(StaticChipList);
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -32,7 +32,8 @@ describe('MdChipList', () => {
|
||||
chipListNativeElement = chipListDebugElement.nativeElement;
|
||||
chipListInstance = chipListDebugElement.componentInstance;
|
||||
testComponent = fixture.debugElement.componentInstance;
|
||||
});
|
||||
chips = chipListInstance.chips;
|
||||
}));
|
||||
|
||||
describe('basic behaviors', () => {
|
||||
it('adds the `md-chip-list` class', () => {
|
||||
@@ -42,12 +43,20 @@ describe('MdChipList', () => {
|
||||
|
||||
describe('focus behaviors', () => {
|
||||
beforeEach(() => {
|
||||
items = chipListInstance.chips;
|
||||
manager = chipListInstance._keyManager;
|
||||
});
|
||||
|
||||
it('focuses the first chip on focus', () => {
|
||||
let FOCUS_EVENT: Event = {} as Event;
|
||||
|
||||
chipListInstance.focus(FOCUS_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(manager.focusedItemIndex).toBe(0);
|
||||
});
|
||||
|
||||
it('watches for chip focus', () => {
|
||||
let array = items.toArray();
|
||||
let array = chips.toArray();
|
||||
let lastIndex = array.length - 1;
|
||||
let lastItem = array[lastIndex];
|
||||
|
||||
@@ -59,7 +68,7 @@ describe('MdChipList', () => {
|
||||
|
||||
describe('on chip destroy', () => {
|
||||
it('focuses the next item', () => {
|
||||
let array = items.toArray();
|
||||
let array = chips.toArray();
|
||||
let midItem = array[2];
|
||||
|
||||
// Focus the middle item
|
||||
@@ -74,7 +83,7 @@ describe('MdChipList', () => {
|
||||
});
|
||||
|
||||
it('focuses the previous item', () => {
|
||||
let array = items.toArray();
|
||||
let array = chips.toArray();
|
||||
let lastIndex = array.length - 1;
|
||||
let lastItem = array[lastIndex];
|
||||
|
||||
@@ -91,20 +100,89 @@ describe('MdChipList', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('keyboard behavior', () => {
|
||||
|
||||
describe('when selectable is true', () => {
|
||||
beforeEach(() => {
|
||||
testComponent.selectable = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('SPACE selects/deselects the currently focused chip', () => {
|
||||
let FOCUS_EVENT: Event = {} as Event;
|
||||
let SPACE_EVENT: KeyboardEvent = new FakeEvent(SPACE) as KeyboardEvent;
|
||||
let firstChip: MdChip = chips.toArray()[0];
|
||||
|
||||
spyOn(testComponent, 'chipSelect');
|
||||
spyOn(testComponent, 'chipDeselect');
|
||||
|
||||
// Make sure we have the first chip focused
|
||||
chipListInstance.focus(FOCUS_EVENT);
|
||||
|
||||
// Use the spacebar to select the chip
|
||||
chipListInstance._keydown(SPACE_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(firstChip.selected).toBeTruthy();
|
||||
expect(testComponent.chipSelect).toHaveBeenCalledTimes(1);
|
||||
expect(testComponent.chipSelect).toHaveBeenCalledWith(0);
|
||||
|
||||
// Use the spacebar to deselect the chip
|
||||
chipListInstance._keydown(SPACE_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(firstChip.selected).toBeFalsy();
|
||||
expect(testComponent.chipDeselect).toHaveBeenCalledTimes(1);
|
||||
expect(testComponent.chipDeselect).toHaveBeenCalledWith(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when selectable is false', () => {
|
||||
beforeEach(() => {
|
||||
testComponent.selectable = false;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('SPACE ignores selection', () => {
|
||||
let FOCUS_EVENT: Event = {} as Event;
|
||||
let SPACE_EVENT: KeyboardEvent = new FakeEvent(SPACE) as KeyboardEvent;
|
||||
let firstChip: MdChip = chips.toArray()[0];
|
||||
|
||||
spyOn(testComponent, 'chipSelect');
|
||||
|
||||
// Make sure we have the first chip focused
|
||||
chipListInstance.focus(FOCUS_EVENT);
|
||||
|
||||
// Use the spacebar to attempt to select the chip
|
||||
chipListInstance._keydown(SPACE_EVENT);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(firstChip.selected).toBeFalsy();
|
||||
expect(testComponent.chipSelect).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<md-chip-list>
|
||||
<div *ngIf="remove != 0"><md-chip>{{name}} 1</md-chip></div>
|
||||
<div *ngIf="remove != 1"><md-chip>{{name}} 2</md-chip></div>
|
||||
<div *ngIf="remove != 2"><md-chip>{{name}} 3</md-chip></div>
|
||||
<div *ngIf="remove != 3"><md-chip>{{name}} 4</md-chip></div>
|
||||
<div *ngIf="remove != 4"><md-chip>{{name}} 5</md-chip></div>
|
||||
</md-chip-list>
|
||||
`
|
||||
<md-chip-list [selectable]="selectable">
|
||||
<div *ngFor="let i of [0,1,2,3,4]">
|
||||
<div *ngIf="remove != i">
|
||||
<md-chip (select)="chipSelect(i)" (deselect)="chipDeselect(i)">
|
||||
{{name}} {{i + 1}}
|
||||
</md-chip>
|
||||
</div>
|
||||
</div>
|
||||
</md-chip-list>`
|
||||
})
|
||||
class StaticChipList {
|
||||
name: 'Test';
|
||||
name: string = 'Test';
|
||||
selectable: boolean = true;
|
||||
remove: Number;
|
||||
|
||||
chipSelect(index: Number) {}
|
||||
chipDeselect(index: Number) {}
|
||||
}
|
||||
|
||||
+72
-13
@@ -4,6 +4,7 @@ import {
|
||||
Component,
|
||||
ContentChildren,
|
||||
ElementRef,
|
||||
Input,
|
||||
ModuleWithProviders,
|
||||
NgModule,
|
||||
QueryList,
|
||||
@@ -12,6 +13,8 @@ import {
|
||||
|
||||
import {MdChip} from './chip';
|
||||
import {ListKeyManager} from '../core/a11y/list-key-manager';
|
||||
import {coerceBooleanProperty} from '../core/coercion/boolean-property';
|
||||
import {SPACE} from '../core/keyboard/keycodes';
|
||||
|
||||
/**
|
||||
* A material design chips component (named ChipList for it's similarity to the List component).
|
||||
@@ -34,8 +37,8 @@ import {ListKeyManager} from '../core/a11y/list-key-manager';
|
||||
'class': 'md-chip-list',
|
||||
|
||||
// Events
|
||||
'(focus)': '_keyManager.focusFirstItem()',
|
||||
'(keydown)': 'keydown($event)'
|
||||
'(focus)': 'focus($event)',
|
||||
'(keydown)': '_keydown($event)'
|
||||
},
|
||||
queries: {
|
||||
chips: new ContentChildren(MdChip)
|
||||
@@ -49,29 +52,85 @@ export class MdChipList implements AfterContentInit {
|
||||
/** Track which chips we're listening to for focus/destruction. */
|
||||
private _subscribed: WeakMap<MdChip, boolean> = new WeakMap();
|
||||
|
||||
/** Whether or not the chip is selectable. */
|
||||
protected _selectable: boolean = true;
|
||||
|
||||
/** The ListKeyManager which handles focus. */
|
||||
_keyManager: ListKeyManager;
|
||||
|
||||
/** The chip components contained within this chip list. */
|
||||
chips: QueryList<MdChip>;
|
||||
|
||||
constructor(private _elementRef: ElementRef) {}
|
||||
constructor(private _elementRef: ElementRef) {
|
||||
}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this._keyManager = new ListKeyManager(this.chips).withFocusWrap();
|
||||
|
||||
// Go ahead and subscribe all of the initial chips
|
||||
this.subscribeChips(this.chips);
|
||||
this._subscribeChips(this.chips);
|
||||
|
||||
// When the list changes, re-subscribe
|
||||
this.chips.changes.subscribe((chips: QueryList<MdChip>) => {
|
||||
this.subscribeChips(chips);
|
||||
this._subscribeChips(chips);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this chip is selectable. When a chip is not selectable,
|
||||
* it's selected state is always ignored.
|
||||
*/
|
||||
@Input() get selectable(): boolean {
|
||||
return this._selectable;
|
||||
}
|
||||
|
||||
set selectable(value: boolean) {
|
||||
this._selectable = coerceBooleanProperty(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically focus the chip list. This in turn focuses the first non-disabled chip in this
|
||||
* chip list.
|
||||
*
|
||||
* TODO: ARIA says this should focus the first `selected` chip.
|
||||
*/
|
||||
focus(event: Event) {
|
||||
this._keyManager.focusFirstItem();
|
||||
}
|
||||
|
||||
/** Pass relevant key presses to our key manager. */
|
||||
keydown(event: KeyboardEvent) {
|
||||
this._keyManager.onKeydown(event);
|
||||
_keydown(event: KeyboardEvent) {
|
||||
switch (event.keyCode) {
|
||||
case SPACE:
|
||||
// If we are selectable, toggle the focused chip
|
||||
if (this.selectable) {
|
||||
this._toggleSelectOnFocusedChip();
|
||||
}
|
||||
|
||||
// Always prevent space from scrolling the page since the list has focus
|
||||
event.preventDefault();
|
||||
break;
|
||||
default:
|
||||
this._keyManager.onKeydown(event);
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggles the selected state of the currently focused chip. */
|
||||
protected _toggleSelectOnFocusedChip(): void {
|
||||
// Allow disabling of chip selection
|
||||
if (!this.selectable) {
|
||||
return;
|
||||
}
|
||||
|
||||
let focusedIndex = this._keyManager.focusedItemIndex;
|
||||
|
||||
if (this._isValidIndex(focusedIndex)) {
|
||||
let focusedChip: MdChip = this.chips.toArray()[focusedIndex];
|
||||
|
||||
if (focusedChip) {
|
||||
focusedChip.toggleSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,8 +139,8 @@ export class MdChipList implements AfterContentInit {
|
||||
*
|
||||
* @param chips The list of chips to be subscribed.
|
||||
*/
|
||||
protected subscribeChips(chips: QueryList<MdChip>): void {
|
||||
chips.forEach(chip => this.addChip(chip));
|
||||
protected _subscribeChips(chips: QueryList<MdChip>): void {
|
||||
chips.forEach(chip => this._addChip(chip));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +151,7 @@ export class MdChipList implements AfterContentInit {
|
||||
* @param chip The chip to be subscribed (or checked for existing
|
||||
* subscription).
|
||||
*/
|
||||
protected addChip(chip: MdChip) {
|
||||
protected _addChip(chip: MdChip) {
|
||||
// If we've already been subscribed to a parent, do nothing
|
||||
if (this._subscribed.has(chip)) {
|
||||
return;
|
||||
@@ -102,7 +161,7 @@ export class MdChipList implements AfterContentInit {
|
||||
chip.onFocus.subscribe(() => {
|
||||
let chipIndex: number = this.chips.toArray().indexOf(chip);
|
||||
|
||||
if (this.isValidIndex(chipIndex)) {
|
||||
if (this._isValidIndex(chipIndex)) {
|
||||
this._keyManager.updateFocusedItemIndex(chipIndex);
|
||||
}
|
||||
});
|
||||
@@ -111,7 +170,7 @@ export class MdChipList implements AfterContentInit {
|
||||
chip.destroy.subscribe(() => {
|
||||
let chipIndex: number = this.chips.toArray().indexOf(chip);
|
||||
|
||||
if (this.isValidIndex(chipIndex)) {
|
||||
if (this._isValidIndex(chipIndex)) {
|
||||
// Check whether the chip is the last item
|
||||
if (chipIndex < this.chips.length - 1) {
|
||||
this._keyManager.setFocus(chipIndex);
|
||||
@@ -133,7 +192,7 @@ export class MdChipList implements AfterContentInit {
|
||||
* @param index The index to be checked.
|
||||
* @returns {boolean} True if the index is valid for our list of chips.
|
||||
*/
|
||||
private isValidIndex(index: number): boolean {
|
||||
private _isValidIndex(index: number): boolean {
|
||||
return index >= 0 && index < this.chips.length;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {Component, DebugElement} from '@angular/core';
|
||||
import {By} from '@angular/platform-browser';
|
||||
import {MdChipList, MdChip, MdChipsModule} from './index';
|
||||
import {MdChipList, MdChip, MdChipEvent, MdChipsModule} from './index';
|
||||
|
||||
describe('Chips', () => {
|
||||
let fixture: ComponentFixture<any>;
|
||||
@@ -86,6 +86,28 @@ describe('Chips', () => {
|
||||
|
||||
expect(testComponent.chipDestroy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('allows color customization', () => {
|
||||
expect(chipNativeElement.classList).toContain('md-primary');
|
||||
|
||||
testComponent.color = 'warn';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(chipNativeElement.classList).not.toContain('md-primary');
|
||||
expect(chipNativeElement.classList).toContain('md-warn');
|
||||
});
|
||||
|
||||
it('allows selection', () => {
|
||||
spyOn(testComponent, 'chipSelect');
|
||||
expect(chipNativeElement.classList).not.toContain('md-chip-selected');
|
||||
|
||||
testComponent.selected = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(chipNativeElement.classList).toContain('md-chip-selected');
|
||||
expect(testComponent.chipSelect).toHaveBeenCalledWith({ chip: chipInstance });
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -94,20 +116,30 @@ describe('Chips', () => {
|
||||
template: `
|
||||
<md-chip-list>
|
||||
<div *ngIf="shouldShow">
|
||||
<md-chip (focus)="chipFocus($event)" (destroy)="chipDestroy($event)">
|
||||
<md-chip [color]="color" [selected]="selected"
|
||||
(focus)="chipFocus($event)" (destroy)="chipDestroy($event)"
|
||||
(select)="chipSelect($event)" (deselect)="chipDeselect($event)">
|
||||
{{name}}
|
||||
</md-chip>
|
||||
</div>
|
||||
</md-chip-list>`
|
||||
})
|
||||
class SingleChip {
|
||||
name: String = 'Test';
|
||||
shouldShow: Boolean = true;
|
||||
name: string = 'Test';
|
||||
color: string = 'primary';
|
||||
selected: boolean = false;
|
||||
shouldShow: boolean = true;
|
||||
|
||||
chipFocus() {
|
||||
chipFocus(event: MdChipEvent) {
|
||||
}
|
||||
|
||||
chipDestroy() {
|
||||
chipDestroy(event: MdChipEvent) {
|
||||
}
|
||||
|
||||
chipSelect(event: MdChipEvent) {
|
||||
}
|
||||
|
||||
chipDeselect(event: MdChipEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+76
-16
@@ -17,7 +17,7 @@ export interface MdChipEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* A material design styled Chip component. Used inside the ChipList component.
|
||||
* A material design styled Chip component. Used inside the MdChipList component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'md-basic-chip, [md-basic-chip], md-chip, [md-chip]',
|
||||
@@ -26,6 +26,7 @@ export interface MdChipEvent {
|
||||
'tabindex': '-1',
|
||||
'role': 'option',
|
||||
|
||||
'[class.md-chip-selected]': 'selected',
|
||||
'[attr.disabled]': 'disabled',
|
||||
'[attr.aria-disabled]': '_isAriaDisabled',
|
||||
|
||||
@@ -34,31 +35,37 @@ export interface MdChipEvent {
|
||||
})
|
||||
export class MdChip implements Focusable, OnInit, OnDestroy {
|
||||
|
||||
/* Whether or not the chip is disabled. */
|
||||
/** Whether or not the chip is disabled. Disabled chips cannot be focused. */
|
||||
protected _disabled: boolean = null;
|
||||
|
||||
/**
|
||||
* Emitted when the chip is focused.
|
||||
*/
|
||||
/** 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>();
|
||||
|
||||
/**
|
||||
* Emitted when the chip is destroyed.
|
||||
*/
|
||||
/** Emitted when the chip is selected. */
|
||||
@Output() select = new EventEmitter<MdChipEvent>();
|
||||
|
||||
/** Emitted when the chip is deselected. */
|
||||
@Output() deselect = new EventEmitter<MdChipEvent>();
|
||||
|
||||
/** Emitted when the chip is destroyed. */
|
||||
@Output() destroy = new EventEmitter<MdChipEvent>();
|
||||
|
||||
constructor(protected _renderer: Renderer, protected _elementRef: ElementRef) {}
|
||||
constructor(protected _renderer: Renderer, protected _elementRef: ElementRef) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
let el: HTMLElement = this._elementRef.nativeElement;
|
||||
|
||||
if (el.nodeName.toLowerCase() == 'md-chip' || el.hasAttribute('md-chip')) {
|
||||
el.classList.add('md-chip');
|
||||
}
|
||||
this._addDefaultCSSClass();
|
||||
this._updateColor(this._color);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy.emit({ chip: this });
|
||||
this.destroy.emit({chip: this});
|
||||
}
|
||||
|
||||
/** Whether or not the chip is disabled. */
|
||||
@@ -76,10 +83,40 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
|
||||
return String(coerceBooleanProperty(this.disabled));
|
||||
}
|
||||
|
||||
/** Whether or not this chip is selected. */
|
||||
@Input() get selected(): boolean {
|
||||
return this._selected;
|
||||
}
|
||||
|
||||
set selected(value: boolean) {
|
||||
this._selected = coerceBooleanProperty(value);
|
||||
|
||||
if (this._selected) {
|
||||
this.select.emit({chip: this});
|
||||
} else {
|
||||
this.deselect.emit({chip: this});
|
||||
}
|
||||
}
|
||||
|
||||
/** Toggles the current selected state of this chip. */
|
||||
toggleSelected(): boolean {
|
||||
this.selected = !this.selected;
|
||||
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._renderer.invokeElementMethod(this._elementRef.nativeElement, 'focus');
|
||||
this.onFocus.emit({ chip: this });
|
||||
this.onFocus.emit({chip: this});
|
||||
}
|
||||
|
||||
/** Ensures events fire properly upon click. */
|
||||
@@ -92,4 +129,27 @@ export class MdChip implements Focusable, OnInit, OnDestroy {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializes the appropriate CSS classes based on the chip type (basic or standard). */
|
||||
private _addDefaultCSSClass() {
|
||||
let el: HTMLElement = this._elementRef.nativeElement;
|
||||
|
||||
if (el.nodeName.toLowerCase() == 'md-chip' || el.hasAttribute('md-chip')) {
|
||||
el.classList.add('md-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 md-color on the native element. */
|
||||
private _setElementColor(color: string, isAdd: boolean) {
|
||||
if (color != null && color != '') {
|
||||
this._renderer.setElementClass(this._elementRef.nativeElement, `md-${color}`, isAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class FakeQueryList<T> extends QueryList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
class FakeEvent {
|
||||
export class FakeEvent {
|
||||
defaultPrevented: boolean = false;
|
||||
constructor(public keyCode: number) {}
|
||||
preventDefault() {
|
||||
|
||||
Reference in New Issue
Block a user