diff --git a/src/demo-app/chips/chips-demo.html b/src/demo-app/chips/chips-demo.html new file mode 100644 index 000000000000..6e862c6095c7 --- /dev/null +++ b/src/demo-app/chips/chips-demo.html @@ -0,0 +1,11 @@ +
+
+

Static Chips

+ + + Chip 1 + Chip 2 + Chip 3 + +
+
\ No newline at end of file diff --git a/src/demo-app/chips/chips-demo.scss b/src/demo-app/chips/chips-demo.scss new file mode 100644 index 000000000000..996d6cfd641f --- /dev/null +++ b/src/demo-app/chips/chips-demo.scss @@ -0,0 +1,2 @@ +.chips-demo { +} \ No newline at end of file diff --git a/src/demo-app/chips/chips-demo.ts b/src/demo-app/chips/chips-demo.ts new file mode 100644 index 000000000000..f616fd10204e --- /dev/null +++ b/src/demo-app/chips/chips-demo.ts @@ -0,0 +1,57 @@ +import {Component} from '@angular/core'; + +export interface Person { + name: string; +} + +@Component({ + moduleId: module.id, + selector: 'chips-demo', + templateUrl: 'chips-demo.html', + styleUrls: ['chips-demo.scss'] +}) +export class ChipsDemo { + people:Person[] = [ + { name: 'Kara' }, + { name: 'Jeremy' }, + { name: 'Topher' }, + { name: 'Elad' }, + { name: 'Kristiyan' }, + { name: 'Paul' } + ]; + favorites:Person[] = []; + + alert(message:string): void { + alert(message); + } + + add(input:HTMLInputElement):void { + if (input.value && input.value.trim() != '') { + this.people.push({ name: input.value.trim() }); + input.value = ''; + } + } + + remove(person:Person): void { + var index = this.people.indexOf(person); + + if (index > -1 && index < this.people.length) { + this.people.splice(index, 1); + } + + // We should unfavorite them if they are no longer a contributor + this.unfavorite(person); + } + + favorite(person:Person): void { + this.favorites.push(person); + } + + unfavorite(person:Person): void { + var index = this.favorites.indexOf(person); + + if (index > -1 && index < this.favorites.length) { + this.favorites.splice(index, 1); + } + } +} \ No newline at end of file diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index 048530e046fe..74191dd26cf0 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -13,6 +13,7 @@ import {IconDemo} from './icon/icon-demo'; import {GesturesDemo} from './gestures/gestures-demo'; import {InputDemo} from './input/input-demo'; import {CardDemo} from './card/card-demo'; +import {ChipsDemo} from './chips/chips-demo'; import {RadioDemo} from './radio/radio-demo'; import {ButtonToggleDemo} from './button-toggle/button-toggle-demo'; import {ProgressCircleDemo} from './progress-circle/progress-circle-demo'; @@ -48,6 +49,7 @@ import {TabsDemo, SunnyTabContent, RainyTabContent, FoggyTabContent} from './tab ButtonDemo, ButtonToggleDemo, CardDemo, + ChipsDemo, CheckboxDemo, DemoApp, DialogDemo, diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 5a10843543af..c13da3e1886a 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -23,6 +23,7 @@ export class DemoApp { {name: 'Button', route: 'button'}, {name: 'Button Toggle', route: 'button-toggle'}, {name: 'Card', route: 'card'}, + {name: 'Chips', route: 'chips'}, {name: 'Checkbox', route: 'checkbox'}, {name: 'Dialog', route: 'dialog'}, {name: 'Gestures', route: 'gestures'}, diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index 68ec614443db..c9c36d5a6561 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -22,6 +22,7 @@ import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo'; import {SliderDemo} from '../slider/slider-demo'; import {RadioDemo} from '../radio/radio-demo'; import {CardDemo} from '../card/card-demo'; +import {ChipsDemo} from '../chips/chips-demo'; import {MenuDemo} from '../menu/menu-demo'; import {RippleDemo} from '../ripple/ripple-demo'; import {DialogDemo} from '../dialog/dialog-demo'; @@ -33,6 +34,7 @@ export const DEMO_APP_ROUTES: Routes = [ {path: '', component: Home}, {path: 'button', component: ButtonDemo}, {path: 'card', component: CardDemo}, + {path: 'chips', component: ChipsDemo}, {path: 'radio', component: RadioDemo}, {path: 'select', component: SelectDemo}, {path: 'sidenav', component: SidenavDemo}, diff --git a/src/lib/chips/README.md b/src/lib/chips/README.md new file mode 100644 index 000000000000..8cad19fae5ff --- /dev/null +++ b/src/lib/chips/README.md @@ -0,0 +1,7 @@ +# md-chips + +`md-chips` provides a horizontal display of (optionally) selectable, addable, and removable, +items and an input to create additional ones (again; optional). You can read more about chips +in the [Material Design spec](https://material.google.com/components/chips.html). + +This is a placeholder README for the eventual chips component. diff --git a/src/lib/chips/chip.ts b/src/lib/chips/chip.ts new file mode 100644 index 000000000000..ba58796f37cd --- /dev/null +++ b/src/lib/chips/chip.ts @@ -0,0 +1,26 @@ +import { + Component, + ElementRef, + Renderer +} from '@angular/core'; + +@Component(MdChip.COMPONENT_CONFIG) +export class MdChip { + + static COMPONENT_CONFIG:Component = { + selector: 'md-chip, [md-chip]', + template: ``, + host: { + // Properties + 'tabindex': '-1', + 'role': 'option' + } + }; + + constructor(protected _renderer: Renderer, protected _elementRef: ElementRef) { + } + + ngAfterContentInit(): void { + this._elementRef.nativeElement.classList.add('md-chip'); + } +} diff --git a/src/lib/chips/chips.scss b/src/lib/chips/chips.scss new file mode 100644 index 000000000000..9bd8922e523e --- /dev/null +++ b/src/lib/chips/chips.scss @@ -0,0 +1,18 @@ +$md-chip-vertical-padding: 8px; +$md-chip-horizontal-padding: 12px; + +.md-chips { + padding: $md-chip-horizontal-padding; +} + +.md-chip { + display: inline-block; + padding: $md-chip-vertical-padding $md-chip-horizontal-padding + $md-chip-vertical-padding $md-chip-horizontal-padding; + border-radius: $md-chip-horizontal-padding * 2; + + background-color: #E0E0E0; + color: rgba(0, 0, 0, 0.87); + font-size: 13px; + line-height: 16px; +} diff --git a/src/lib/chips/chips.ts b/src/lib/chips/chips.ts new file mode 100644 index 000000000000..271b50f4ff3c --- /dev/null +++ b/src/lib/chips/chips.ts @@ -0,0 +1,55 @@ +import { + ChangeDetectionStrategy, + Component, + ContentChildren, + ElementRef, + ModuleWithProviders, + NgModule, + QueryList, + ViewEncapsulation +} from '@angular/core'; + +import {MdChip} from './chip'; + +@Component(MdChips.COMPONENT_CONFIG) +export class MdChips { + + static COMPONENT_CONFIG: Component = { + moduleId: module.id, + selector: 'md-chips', + template: ``, + host: { + // Properties + 'tabindex': '0', + 'role': 'listbox', + }, + queries: { + items: new ContentChildren(MdChip) + }, + styleUrls: ['chips.css'], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush + }; + + protected items: QueryList; + + constructor(private _elementRef: ElementRef) {} + + ngAfterContentInit(): void { + this._elementRef.nativeElement.classList.add('md-chips'); + } +} + +@NgModule({ + imports: [], + exports: [MdChips, MdChip], + declarations: [MdChips, MdChip] +}) +export class MdChipsModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: MdChipsModule, + providers: [] + } + } +} \ No newline at end of file diff --git a/src/lib/chips/index.ts b/src/lib/chips/index.ts new file mode 100644 index 000000000000..7861dd3a337b --- /dev/null +++ b/src/lib/chips/index.ts @@ -0,0 +1,2 @@ +export * from './chips'; +export * from './chip'; diff --git a/src/lib/core/a11y/list-key-manager.ts b/src/lib/core/a11y/list-key-manager.ts index 8e447ff0c327..c132dc714ac9 100644 --- a/src/lib/core/a11y/list-key-manager.ts +++ b/src/lib/core/a11y/list-key-manager.ts @@ -32,10 +32,18 @@ export class ListKeyManager { return this; } - /** Sets the focus of the list to the item at the index specified. */ - setFocus(index: number): void { + /** + * Sets the focus of the list to the item at the index specified. + * + * @param index The index of the item to be focused. + * @param focusElement Whether or not to focus the element as well. Defaults to `true`. + */ + setFocus(index: number, focusElement: boolean = true): void { this._focusedItemIndex = index; - this._items.toArray()[index].focus(); + + if (focusElement) { + this._items.toArray()[index].focus(); + } } /** Sets the focus properly depending on the key event passed in. */ @@ -43,15 +51,19 @@ export class ListKeyManager { switch (event.keyCode) { case DOWN_ARROW: this.focusNextItem(); + event.preventDefault(); break; case UP_ARROW: this.focusPreviousItem(); + event.preventDefault(); break; case HOME: this.focusFirstItem(); + event.preventDefault(); break; case END: this.focusLastItem(); + event.preventDefault(); break; case TAB: this._tabOut.next(null); diff --git a/src/lib/core/keyboard/keycodes.ts b/src/lib/core/keyboard/keycodes.ts index 6204987a0e8a..ab287296e9c8 100644 --- a/src/lib/core/keyboard/keycodes.ts +++ b/src/lib/core/keyboard/keycodes.ts @@ -18,3 +18,6 @@ export const END = 35; export const ENTER = 13; export const SPACE = 32; export const TAB = 9; + +export const BACKSPACE = 8; +export const DELETE = 46; diff --git a/src/lib/index.ts b/src/lib/index.ts index e242a9fc8777..7202a6b57f55 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,6 +4,7 @@ export * from './module'; export * from './button/index'; export * from './button-toggle/index'; export * from './card/index'; +export * from './chips/index'; export * from './checkbox/index'; export * from './dialog/index'; export * from './grid-list/index'; diff --git a/src/lib/module.ts b/src/lib/module.ts index 754226b7f0c6..1f3ebbe62610 100644 --- a/src/lib/module.ts +++ b/src/lib/module.ts @@ -20,6 +20,7 @@ import {MdSidenavModule} from './sidenav/index'; import {MdListModule} from './list/index'; import {MdGridListModule} from './grid-list/index'; import {MdCardModule} from './card/index'; +import {MdChipsModule} from './chips/index'; import {MdIconModule} from './icon/index'; import {MdProgressCircleModule} from './progress-circle/index'; import {MdProgressBarModule} from './progress-bar/index'; @@ -36,6 +37,7 @@ const MATERIAL_MODULES = [ MdButtonModule, MdButtonToggleModule, MdCardModule, + MdChipsModule, MdCheckboxModule, MdDialogModule, MdGridListModule, @@ -66,6 +68,7 @@ const MATERIAL_MODULES = [ imports: [ MdButtonModule.forRoot(), MdCardModule.forRoot(), + MdChipsModule.forRoot(), MdCheckboxModule.forRoot(), MdGridListModule.forRoot(), MdInputModule.forRoot(),