diff --git a/src/components/button/button.html b/src/components/button/button.html new file mode 100644 index 00000000000..4e3df0e2266 --- /dev/null +++ b/src/components/button/button.html @@ -0,0 +1 @@ + diff --git a/src/components/button/button.scss b/src/components/button/button.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/components/button/button.spec.ts b/src/components/button/button.spec.ts new file mode 100644 index 00000000000..44347a7be1d --- /dev/null +++ b/src/components/button/button.spec.ts @@ -0,0 +1,49 @@ +import { + it, + iit, + describe, + ddescribe, + expect, + inject, + injectAsync, + TestComponentBuilder, + beforeEachProviders, + beforeEach, +} from 'angular2/testing'; +import {provide, Component} from 'angular2/core'; +import {MdButton} from './button'; + + +describe('MdButton', () => { + let builder: TestComponentBuilder; + + beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { builder = tcb; })); + + it('should ...', () => { + return builder.createAsync(MdButton).then((fixture) => { + fixture.detectChanges(); + }); + }); + + + + +}); + + + +/** Test component that contains an MdButton. */ +@Component({ + selector: 'test-app', + directives: [MdButton], + template: + ``, +}) +class TestApp { + clickCount: number = 0; + isDisabled: boolean = false; + + increment() { + this.clickCount++; + } +} diff --git a/src/components/button/button.ts b/src/components/button/button.ts new file mode 100644 index 00000000000..2a4a8e04838 --- /dev/null +++ b/src/components/button/button.ts @@ -0,0 +1,95 @@ +import {Component, View, ViewEncapsulation, OnChanges, SimpleChange} from 'angular2/core'; + + +// TODO(jelbourn): Ink ripples. +// TODO(jelbourn): Make the `isMouseDown` stuff done with one global listener. + +@Component({ + selector: '[md-button]:not(a), [md-raised-button]:not(a), [md-fab]:not(a)', + host: { + '(mousedown)': 'onMousedown()', + '(focus)': 'onFocus()', + '(blur)': 'onBlur()', + '[class.md-button-focus]': 'isKeyboardFocused', + }, +}) +@View({ + templateUrl: 'package:angular2_material/src/components/button/button.html', + styleUrls: ['package:angular2_material/src/components/button/button.css'], + encapsulation: ViewEncapsulation.None, +}) +export class MdButton { + /** Whether a mousedown has occured on this element in the last 100ms. */ + isMouseDown: boolean = false; + + /** Whether the button has focus from the keyboard (not the mouse). Used for class binding. */ + isKeyboardFocused: boolean = false; + + onMousedown() { + // We only *show* the focus style when focus has come to the button via the keyboard. + // The Material Design spec is silent on this topic, and without doing this, the + // button continues to look :active after clicking. + // @see http://marcysutton.com/button-focus-hell/ + this.isMouseDown = true; + setTimeout(() => {this.isMouseDown = false}, 100); + } + + onFocus() { + this.isKeyboardFocused = !this.isMouseDown; + } + + onBlur() { + this.isKeyboardFocused = false; + } +} + + +@Component({ + selector: 'a[md-button], a[md-raised-button], a[md-fab]', + inputs: ['disabled'], + host: { + '(click)': 'onClick($event)', + '(mousedown)': 'onMousedown()', + '(focus)': 'onFocus()', + '(blur)': 'onBlur()', + '[tabIndex]': 'tabIndex', + '[class.md-button-focus]': 'isKeyboardFocused', + '[attr.aria-disabled]': 'isAriaDisabled', + }, +}) +@View({ + templateUrl: 'package:angular2_material/src/components/button/button.html', + encapsulation: ViewEncapsulation.None +}) +export class MdAnchor extends MdButton implements OnChanges { + tabIndex: number; + disabled_: boolean; + + get disabled(): boolean { + return this.disabled_; + } + + set disabled(value) { + // The presence of *any* disabled value makes the component disabled, *except* for false. + this.disabled_ = value != null && this.disabled !== false; + } + + onClick(event: Event) { + // A disabled anchor shouldn't navigate anywhere. + if (this.disabled) { + event.preventDefault(); + } + } + + /** Invoked when a change is detected. */ + ngOnChanges(changes: {[propName: string]: SimpleChange}) { + // A disabled anchor should not be in the tab flow. + this.tabIndex = this.disabled ? -1 : 0; + } + + /** Gets the aria-disabled value for the component, which must be a string for Dart. */ + get isAriaDisabled(): string { + return this.disabled ? 'true' : 'false'; + } +} +