feat(material): first ng2 material design components

This commit is contained in:
Jeremy Elbourn 2015-02-17 11:56:24 -08:00 committed by Yegor Jbanov
parent ffe13078e5
commit f149ae79c6
64 changed files with 4003 additions and 7 deletions

View File

@ -1,6 +1,8 @@
var autoprefixer = require('gulp-autoprefixer');
var format = require('gulp-clang-format');
var gulp = require('gulp');
var gulpPlugins = require('gulp-load-plugins')();
var sass = require('gulp-sass');
var shell = require('gulp-shell');
var runSequence = require('run-sequence');
var madge = require('madge');
@ -152,7 +154,7 @@ var CONFIG = {
src: ['modules/**/*.css'],
pipes: {}
}
},
}
},
multicopy: {
js: {
@ -697,3 +699,23 @@ gulp.task('build.js', ['build.js.dev', 'build.js.prod', 'build.js.cjs', 'bundle.
gulp.task('clean', ['build/clean.js', 'build/clean.dart', 'build/clean.docs']);
gulp.task('build', ['build.js', 'build.dart']);
// ------------
// angular material testing rules
gulp.task('build/css.js.dev', function() {
return gulp.src('modules/*/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest(CONFIG.dest.js.dev.es5));
});
// TODO: this target is temporary until we find a way to use the SASS transformer
gulp.task('build/css.dart', function() {
return gulp.src('dist/dart/angular2_material/lib/src/**/*.scss')
.pipe(sass())
.pipe(autoprefixer())
.pipe(gulp.dest('dist/dart/angular2_material/lib/src'));
});
gulp.task('build.material', ['build.js.dev', 'build/css.js.dev']);

View File

@ -1,5 +1,6 @@
import {Injector, bind, OpaqueToken} from 'angular2/di';
import {Type, isBlank, isPresent, BaseException, assertionsEnabled, print, stringify} from 'angular2/src/facade/lang';
import {Injector, bind, OpaqueToken, Optional} from 'angular2/di';
import {NumberWrapper, Type, isBlank, isPresent, BaseException,
assertionsEnabled, print, stringify} from 'angular2/src/facade/lang';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Compiler, CompilerCache} from './compiler/compiler';

View File

@ -10,6 +10,8 @@ class Math {
return math.pow(x, exponent);
}
static num max(num a, num b) => math.max(a, b);
static num min(num a, num b) => math.min(a, b);
static num floor(num a) => a.floor();

View File

@ -0,0 +1 @@
Temporary home of Material Design components for Angular 2.

View File

@ -0,0 +1,17 @@
{
"name": "angular2_material",
"version": "<%= packageJson.version %>",
"description": "Google material design components for Angular 2",
"homepage": "<%= packageJson.homepage %>",
"bugs": "<%= packageJson.bugs %>",
"contributors": <%= JSON.stringify(packageJson.contributors) %>,
"license": "<%= packageJson.license %>",
"dependencies": {
"angular2": "<%= packageJson.version %>",
"traceur": "<%= packageJson.dependencies.traceur %>",
"rtts_assert": "<%= packageJson.version %>",
"rx": "<%= packageJson.dependencies['rx'] %>",
"zone.js": "<%= packageJson.dependencies['zone.js'] %>"
},
"devDependencies": <%= JSON.stringify(packageJson.devDependencies) %>
}

View File

@ -0,0 +1,16 @@
name: angular2_material
version: <%= packageJson.version %>
authors:
<%= Object.keys(packageJson.contributors).map(function(name) {
return '- '+name+' <'+packageJson.contributors[name]+'>';
}).join('\n') %>
description: Google material design components for Angular 2
homepage: <%= packageJson.homepage %>
environment:
sdk: '>=1.9.0-dev.8.0'
dependencies:
angular2: '^<%= packageJson.version %>'
browser: '>=0.10.0 <0.11.0'
dependency_overrides:
angular2:
path: ../angular2

View File

@ -0,0 +1,2 @@
<style>@import "angular2_material/src/components/button/button.css";</style>
<span class="md-button-wrapper"><content></content></span>

View File

@ -0,0 +1,47 @@
import {Component, View} from 'angular2/angular2';
import {PropertySetter, EventEmitter} from 'angular2/src/core/annotations/di';
import {onChange} from 'angular2/src/core/annotations/annotations';
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
@Component({selector: '[md-button]:not([href])'})
@View({templateUrl: 'angular2_material/src/components/button/button.html'})
export class MdButton {
// TODO(jelbourn): Ink ripples.
}
@Component({
selector: '[md-button][href]',
properties: {
'disabled': 'disabled'
},
hostListeners: {'click': 'onClick($event)'},
lifecycle: [onChange]
})
@View({
templateUrl: 'angular2_material/src/components/button/button.html'
})
export class MdAnchor {
tabIndexSetter: Function;
/** Whether the component is disabled. */
disabled: boolean;
constructor(@PropertySetter('tabIndex') tabIndexSetter: Function) {
this.tabIndexSetter = tabIndexSetter;
}
onClick(event) {
// A disabled anchor shouldn't navigate anywhere.
if (isPresent(this.disabled) && this.disabled !== false) {
event.preventDefault();
}
}
/** Invoked when a change is detected. */
onChange(_) {
// A disabled anchor should not be in the tab flow.
this.tabIndexSetter(this.disabled ? -1 : 0);
}
}

View File

@ -0,0 +1,349 @@
@import "../../core/style/variables";
@import "../../core/style/shadows";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
$button-line-height: 25px !default;
$button-padding: 2px 6px 3px !default;
// Fab buttons
$button-fab-width: 56px !default;
$button-fab-height: 56px !default;
$button-fab-padding: 16px !default;
$button-fab-mini-width: 40px !default;
$button-fab-mini-height: 40px !default;
$button-fab-toast-offset: $button-fab-height * 0.75;
/**
* Mixin to create distinct classes for fab positions, e.g. ".md-fab-bottom-right".
*/
@mixin fab-position($spot, $top: auto, $right: auto, $bottom: auto, $left: auto) {
&.md-fab-#{$spot} {
top: $top;
right: $right;
bottom: $bottom;
left: $left;
position: absolute;
}
}
[md-button] {
user-select: none;
// TODO(jelbourn): What canvas?
position: relative; //for child absolute-positioned <canvas>
outline: none;
border: 0;
padding: 6px;
margin: 0;
background: transparent;
white-space: nowrap;
text-align: center;
// TODO(jelbourn): wat?
font-weight: 500;
font-style: inherit;
font-variant: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
text-decoration: none;
cursor: pointer;
overflow: hidden; // for ink containment
transition: box-shadow $swift-ease-out-duration $swift-ease-out-timing-function,
background-color $swift-ease-out-duration $swift-ease-out-timing-function,
transform $swift-ease-out-duration $swift-ease-out-timing-function;
&:focus {
outline: none;
}
&:hover {
text-decoration: none;
}
&.md-cornered {
border-radius: 0;
}
&.md-icon {
padding: 0;
background: none;
}
&.md-raised {
transform: translate3d(0, 0, 0);
&:not([disabled]) {
@extend .md-shadow-bottom-z-1;
}
}
&.md-fab {
// .md-fab-bottom-right
@include fab-position(
bottom-right,
auto,
($button-fab-width - $button-fab-padding) / 2,
($button-fab-height - $button-fab-padding) / 2,
auto);
// .md-fab-bottom-left
@include fab-position(
bottom-left,
auto,
auto,
($button-fab-height - $button-fab-padding) / 2,
($button-fab-width - $button-fab-padding) / 2);
// .md-fab-top-right
@include fab-position(
top-right,
($button-fab-height - $button-fab-padding) / 2,
($button-fab-width - $button-fab-padding) / 2,
auto,
auto);
// .md-fab-top-left
@include fab-position(
top-left,
($button-fab-height - $button-fab-padding) / 2,
auto,
auto,
($button-fab-width - $button-fab-padding) / 2);
z-index: $z-index-fab;
width: $button-fab-width;
height: $button-fab-height;
border-radius: 50%;
@extend .md-shadow-bottom-z-1;
overflow: hidden;
transform: translate3d(0, 0, 0);
transition: 0.2s linear;
transition-property: transform, box-shadow;
// When there is an md-icon inside of an [md-button].
& md-icon {
line-height: $button-fab-height;
margin-top: 0;
}
&.md-mini {
width: $button-fab-mini-width;
height: $button-fab-mini-height;
md-icon {
line-height: $button-fab-mini-height;
}
}
}
&:not([disabled]) {
&.md-raised,
&.md-fab {
&:focus,
&:hover {
transform: translate3d(0, -1px, 0);
@extend .md-shadow-bottom-z-2;
}
}
}
}
// TODO
.md-toast-open-top {
[md-button].md-fab-top-left,
[md-button].md-fab-top-right {
transform: translate3d(0, $button-fab-toast-offset, 0);
&:not([disabled]) {
&:focus,
&:hover {
transform: translate3d(0, $button-fab-toast-offset - 1, 0);
}
}
}
}
// TODO
.md-toast-open-bottom {
[md-button].md-fab-bottom-left,
[md-button].md-fab-bottom-right {
transform: translate3d(0, -$button-fab-toast-offset, 0);
&:not([disabled]) {
&:focus,
&:hover {
transform: translate3d(0, -$button-fab-toast-offset - 1, 0);
}
}
}
}
// TODO
.md-button-group {
display: flex;
flex: 1;
width: 100%;
}
// TODO
.md-button-group > [md-button] {
flex: 1;
display: block;
overflow: hidden;
width: 0;
border-width: 1px 0 1px 1px;
border-radius: 0;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
&:first-child {
border-radius: 2px 0 0 2px;
}
&:last-child {
border-right-width: 1px;
border-radius: 0 2px 2px 0;
}
}
// ------------------------ Button theme ----------------------------------------------------------
$button-border-radius: 3px !default;
$button-fab-border-radius: 50% !default;
// The ELEMENT '[md-button]' is the host.
// The CLASS '[md-button]' is the shadow root.
md-toolbar [md-button].md-fab {
background-color: white;
}
// Because of Shadow DOM emulation, the modifier classes are applied to the [md-button]
// element (host), but the style needs to be changed for the shadow content (.[md-button]).
[md-button] {
border-radius: $button-border-radius;
// When the user explictly applies the primary color.
&.md-primary {
color: md-color($md-primary);
}
&.md-primary.md-fab,
&.md-primary.md-raised {
color: md-color($md-primary, default-contrast);
background-color: md-color($md-primary);
}
// When the user explictly applies the accent color.
&.md-accent {
color: md-color($md-accent);
}
&.md-accent.md-fab,
&.md-accent.md-raised {
color: md-color($md-accent, default-contrast);
background-color: md-color($md-accent);
}
// When the user explictly applies the warn color.
&.md-warn {
color: md-color($md-warn);
}
&.md-warn.md-fab,
&.md-warn.md-raised {
color: md-color($md-warn, default-contrast);
background-color: md-color($md-warn);
}
// When the button is a fab without an explicit color applied.
&.md-fab {
border-radius: $button-fab-border-radius;
background-color: md-color($md-accent);
color: md-color($md-accent, default-contrast);
}
// When the button is raised without an explicit color applied.
&.md-raised {
color: md-color($md-background, default-contrast);
background-color: md-color($md-background, 50);
}
}
// Hover and focus styles (only applied when the button is not disabled).
// The modifiers for :focus and :hover actually apply to the element in the
// shadow DOM (the .[md-button]).
[md-button]:not([disabled]) {
// Default hover/focus background.
&:focus,
&:hover {
background-color: md-color($md-background, 500, 0.2);
}
&.md-primary.md-fab,
&.md-primary.md-raised {
&:hover,
&:focus {
background-color: md-color($md-primary, 600);
}
}
&.md-accent.md-fab,
&.md-accent.md-raised {
&:hover,
&:focus {
background-color: md-color($md-accent, 700);
}
}
&.md-warn.md-fab,
&.md-warn.md-raised {
&:hover,
&:focus {
background-color: md-color($md-warn, 700);
}
}
&.md-fab {
&:hover,
&:focus {
background-color: md-color($md-accent, A700);
}
}
&.md-raised {
&:hover,
&:focus {
background-color: md-color($md-background, 200);
}
}
}
[md-button][disabled],
[md-button][disabled].md-fab,
[md-button][disabled].md-raised, {
color: md-color($md-foreground, disabled);
background-color: transparent;
cursor: not-allowed;
}

View File

@ -0,0 +1,8 @@
<style>@import "angular2_material/src/components/checkbox/checkbox.css";</style>
<div (^click)="toggle($event)">
<div class="md-checkbox-container">
<div class="md-checkbox-icon"></div>
</div>
<div class="md-checkbox-label"><content></content></div>
</div>

View File

@ -0,0 +1,77 @@
import {Component, View, Attribute, PropertySetter} from 'angular2/angular2';
import {isPresent} from 'angular2/src/facade/lang';
import {KeyCodes} from 'angular2_material/src/core/constants'
@Component({
selector: 'md-checkbox',
properties: {
'checked': 'checked',
'disabled': 'disabled'
},
hostListeners: {
'keydown': 'onKeydown($event)'
}
})
@View({
templateUrl: 'angular2_material/src/components/checkbox/checkbox.html',
directives: []
})
export class MdCheckbox {
/** Whether this checkbox is checked. */
checked_: boolean;
/** Setter for `aria-checked` attribute. */
ariaCheckedSetter: Function;
/** Setter for `aria-disabled` attribute. */
ariaDisabledSetter: Function;
constructor(
@Attribute('tabindex') tabindex: string,
@PropertySetter('tabindex') tabindexSetter: Function,
@PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function) {
this.ariaCheckedSetter = ariaCheckedSetter;
this.ariaDisabledSetter = ariaDisabledSetter;
roleSetter('checkbox');
this.checked = false;
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
}
get checked() {
return this.checked_;
}
set checked(value) {
this.checked_ = value;
this.ariaCheckedSetter(value);
}
get disabled() {
return this.disabled_;
}
set disabled(value) {
this.disabled_ = isPresent(value) && value !== false;
this.ariaDisabledSetter(this.disabled_);
}
onKeydown(event: KeyboardEvent) {
if (event.keyCode == KeyCodes.SPACE) {
event.preventDefault();
this.toggle(event);
}
}
toggle(event) {
if (this.disabled) {
event.stopPropagation();
return;
}
this.checked = !this.checked;
this.ariaCheckedSetter(this.checked);
}
}

View File

@ -0,0 +1,176 @@
@import "../../core/style/variables";
@import "../../core/style/shadows";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
$checkbox-width: 18px !default;
$checkbox-height: $checkbox-width !default;
md-checkbox {
box-sizing: border-box;
display: block;
margin: 15px;
white-space: nowrap;
cursor: pointer;
outline: none;
user-select: none;
*, *:after {
box-sizing: border-box;
}
&[aria-checked="true"] .md-checkbox-icon {
border: none;
}
// Checkbox is disabled.
&[disabled] {
cursor: no-drop;
}
// Checkbox is focused.
&:focus .md-checkbox-label:not(:empty) {
border-color: black;
}
// Checkbox is checked.
&[aria-checked="true"] .md-checkbox-icon:after {
transform: rotate(45deg);
position: absolute;
left: 6px;
top: 2px;
display: table;
width: 6px;
height: 12px;
border: 2px solid;
border-top: 0;
border-left: 0;
content: ' ';
}
}
.md-checkbox-container {
position: relative;
top: 4px;
display: inline-block;
width: $checkbox-width;
height: $checkbox-height;
&:after {
content: '';
position: absolute;
top: -10px;
right: -10px;
bottom: -10px;
left: -10px;
}
.md-ripple-container {
position: absolute;
display: block;
width: auto;
height: auto;
left: -15px;
top: -15px;
right: -15px;
bottom: -15px;
}
}
// Checkbox is not checked.
.md-checkbox-icon {
transition: 240ms;
position: absolute;
top: 0;
left: 0;
width: $checkbox-width;
height: $checkbox-height;
border: 2px solid;
border-radius: 2px;
}
.md-checkbox-label {
border: 1px dotted transparent;
position: relative;
display: inline-block;
margin-left: 10px;
vertical-align: middle;
white-space: normal;
pointer-events: none;
user-select: text;
}
// THEME
// TODO(jelbourn): ripple
md-checkbox {
.md-ripple {
color: md-color($md-accent, 600);
}
&[aria-checked="true"] .md-ripple {
color: md-color($md-background, 600);
}
.md-checkbox-icon {
border-color: md-color($md-foreground, icon);
}
&[aria-checked="true"] .md-checkbox-icon {
background-color: md-color($md-accent, 0.87);
}
&[aria-checked="true"] .md-checkbox-icon:after {
border-color: md-color($md-background, 200);
}
&:not([disabled]) {
&.md-primary {
.md-ripple {
color: md-color($md-primary, 600);
}
&[aria-checked="true"] .md-ripple {
color: md-color($md-background, 600);
}
.md-checkbox-icon {
border-color: md-color($md-foreground, icon);
}
&[aria-checked="true"] .md-checkbox-icon {
background-color: md-color($md-primary, 0.87);
}
&[aria-checked="true"] .md-checkbox-icon:after {
border-color: md-color($md-background, 200);
}
}
&.md-warn {
.md-ripple {
color: md-color($md-warn, 600);
}
.md-checkbox-icon {
border-color: md-color($md-foreground, icon);
}
&[aria-checked="true"] .md-checkbox-icon {
background-color: md-color($md-warn, 0.87);
}
&[aria-checked="true"] .md-checkbox-icon:after {
border-color: md-color($md-background, 200);
}
}
}
&[disabled] {
.md-checkbox-icon {
border-color: md-color($md-foreground, disabled);
}
&[aria-checked="true"] .md-checkbox-icon {
background-color: md-color($md-foreground, disabled);
}
}
}

View File

@ -0,0 +1,68 @@
md-grid-list {
display: block;
position: relative;
}
md-grid-tile {
display: block;
position: absolute;
figure {
display: flex;
position: absolute;
align-items: center;
justify-content: center;
height: 100%;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 0;
margin: 0;
}
// Headers & footers
md-grid-tile-header,
md-grid-tile-footer {
display: flex;
flex-direction: row;
align-items: center;
height: 48px;
color: #fff;
background: rgba(0, 0, 0, 0.18);
overflow: hidden;
// Positioning
position: absolute;
left: 0;
right: 0;
h3,
h4 {
font-weight: 400;
margin: 0 0 0 16px;
}
h3 {
font-size: 14px;
}
h4 {
font-size: 12px;
}
}
md-grid-tile-header {
top: 0;
}
md-grid-tile-footer {
bottom: 0;
}
}

View File

@ -0,0 +1,5 @@
<div class="md-grid-list">
<div class="md-grid-tile">
<figure></figure>
</div>
</div>

View File

@ -0,0 +1,253 @@
import {Component, View, onAllChangesDone, Parent} from 'angular2/angular2';
import {onDestroy, onChange} from 'angular2/src/core/annotations/annotations';
import {ListWrapper} from 'angular2/src/facade/collection';
import {CONST} from 'angular2/src/facade/lang';
import {isPresent, isString, NumberWrapper} from 'angular2/src/facade/lang';
import {PropertySetter} from 'angular2/src/core/annotations/di';
// TODO(jelbourn): Set appropriate aria attributes for grid list elements.
@Component({
selector: 'md-grid-list',
properties: {
'cols': 'cols',
'gutterSize': 'gutter-size'
},
lifecycle: [onChange]
})
@View({
templateUrl: 'angular2_material/src/components/grid_list/grid_list.html'
})
export class MdGridList {
/** List of tiles that are being rendered. */
tiles: Array<MdGridTile>;
/** Number of columns being rendered. Can be either string or number */
cols;
/** Mode used to determine row heights. See RowHeightMode. */
rowHeightMode: string;
/** Fixed row height, as given by the user. Only used for 'fixed' mode. */
fixedRowHeight: number;
/** Ratio width:height given by user to determine row height. Only used for 'ratio' mode.*/
rowHeightRatio: number;
/** The amount of space between tiles. This will be something like '5px' or '2em'. */
gutterSize: string;
/** Array used to track the amount of space available. */
spaceTracker: Array<number>;
constructor() {
this.tiles = [];
}
onAllChangesDone() {
}
onChange(_) {
if (!isPresent(this.spaceTracker)) {
if (isString(this.cols)) {
this.cols = NumberWrapper.parseIntAutoRadix(this.cols);
}
this.spaceTracker = ListWrapper.createFixedSize(this.cols);
ListWrapper.fill(this.spaceTracker, 0);
}
}
/**
* Adds a tile to the grid-list.
* @param tile
*/
addTile(tile: MdGridTile) {
ListWrapper.push(this.tiles, tile);
}
/**
* Removes a tile from the grid-list.
* @param tile
*/
removeTile(tile: MdGridTile) {
ListWrapper.remove(this.tiles, tile);
}
/**
* Change handler invoked when bindings are resolved or when bindings have changed.
* Performs a layout.
*/
performLayout() {
//console.log('laying out!');
}
/**
* Computes the amount of space a single 1x1 tile would take up (width or height).
* Used as a basis for other calculations.
* @param sizePercent Percent of the total grid-list space that one 1x1 tile would take up.
* @param gutterFraction Fraction of the gutter size taken up by one 1x1 tile.
* @return The size of a 1x1 tile as an expression that can be evaluated via CSS calc().
*/
getBaseTileSize(sizePercent: number, gutterFraction: number): string {
// Take the base size percent (as would be if evenly dividing the size between cells),
// and then subtracting the size of one gutter. However, since there are no gutters on the
// edges, each tile only uses a fration (gutterShare = numGutters / numCells) of the gutter
// size. (Imagine having one gutter per tile, and then breaking up the extra gutter on the
// edge evenly among the cells).
return `${sizePercent}% - ( ${this.gutterSize} * ${gutterFraction} )`;
}
/**
* Gets The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value.
* @param offset Number of tiles that have already been rendered in the row/column.
* @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).
* @return Position of the tile as a CSS calc() expression.
*/
getTilePosition(baseSize: string, offset: number): string {
// The position comes the size of a 1x1 tile plus gutter for each previous tile in the
// row/column (offset).
return `calc( (${baseSize} + ${this.gutterSize}) * ${offset} )`;
}
/**
* Gets the actual size of a tile, e.g., width or height, taking rowspan or colspan into account.
* @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).
* @param span The tile's rowspan or colspan.
* @return Size of the tile as a CSS calc() expression.
*/
getTileSize(baseSize: string, span: number): string {
return `calc( (${baseSize} * ${span}) + (${span - 1} * ${this.gutterSize}) )`;
}
getTileStyle(tile: MdGridTile, rowIndex: number, colIndex: number): TileStyle {
// Percent of the available horizontal space that one column takes up.
var percentWidthPerTile = this.cols / 100;
// Fraction of the gutter size that each column takes up.
// For example, if there are 5 columns, each column uses 4/5 = 0.8 times the gutter width.
var gutterWidthFractionPerTile = (this.cols - 1) / this.cols;
// Base horizontal size of a column.
var baseTileWidth = getBaseTileSize(percentWidthPerTile, gutterWidthFractionPerTile);
// The width and horizontal position of each tile is always calculated the same way, but the
// height and vertical position depends on the rowMode.
var tileStyle = new TileStyle();
tileStyle.left = getTilePosition(baseTileWidth, colIndex);
tileStyle.width = getTileSize(baseTileWidth, tile.colspan);
// TODO: make cases enums when we support enums
switch (this.rowHeightMode) {
case 'fixed':
// In fixed mode, simply use the given row height.
tileStyle.top = getTilePosition(this.fixedRowHeight, rowIndex);
tileStyle.height = getTileSize(this.fixedRowHeight, tile.rowspan);
break;
case 'ratio':
var percentHeightPerTile = percentWidthPerTile / this.rowHeightRatio;
let baseTileHeight = getBaseTileSize(percentHeightPerTile, gutterWidthFractionPerTile);
// Use paddingTop and marginTop to maintain the given aspect ratio, as
// a percentage-based value for these properties is applied to the *width* of the
// containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties
tileStyle.marginTop = getTilePosition(baseTileHeight, rowIndex);
tileStyle.paddingTop = getTileSize(baseTileHeight, tile.rowspan);
break;
case 'fit':
var percentHeightPerTile;
break;
}
return tileStyle;
}
}
@Component({
selector: 'md-grid-tile',
properties: {
'rowspan': 'rowspan',
'colspan': 'colspan'
},
lifecycle: [onDestroy, onChange]
})
@View({
template: `<figure><content></content></figure>`
})
export class MdGridTile {
gridList: MdGridList;
rowspan: number;
colspan: number;
heightSetter;
widthSetter;
topSetter;
leftSetter;
marginTopSetter;
paddingTopSetter;
isRegisteredWithGridList: boolean;
constructor(
@Parent() gridList: MdGridList,
@PropertySetter('style.height') heightSetter: Function,
@PropertySetter('style.width') widthSetter: Function,
@PropertySetter('style.top') topSetter: Function,
@PropertySetter('style.left') leftSetter: Function,
@PropertySetter('style.marginTop') marginTopSetter: Function,
@PropertySetter('style.paddingTop') paddingTopSetter: Function,
@PropertySetter('role') roleSetter: Function
) {
this.gridList = gridList;
this.heightSetter = heightSetter;
this.widthSetter = widthSetter;
this.topSetter = topSetter;
this.leftSetter = leftSetter;
this.marginTopSetter = marginTopSetter;
this.paddingTopSetter = paddingTopSetter;
roleSetter('listitem');
// Tiles default to 1x1, but rowspan and colspan can be changed via binding.
this.rowspan = 1;
this.colspan = 1;
// DEBUG
heightSetter(`${gridList.tiles.length * 100}px`);
}
/**
* Change handler invoked when bindings are resolved or when bindings have changed.
* Notifies grid-list that a re-layout is required.
*/
onChange(_) {
//console.log(`grid-tile on-change ${this.gridList.tiles.indexOf(this)}`);
if (!this.isRegisteredWithGridList) {
this.gridList.addTile(this);
this.isRegisteredWithGridList = true;
} else {
this.gridList.performLayout();
}
}
/**
* Destructor function. Deregisters this tile from the containing grid-list.
*/
onDestroy() {
this.gridList.removeTile(this);
}
}
/** Simple data structure for style values to be applied to a tile. */
class TileStyle {
height: string;
width: string;
top: string;
left: string;
marginTop: string;
paddingTop: string;
}

View File

@ -0,0 +1,18 @@
import {Decorator} from 'angular2/angular2';
import {PropertySetter} from 'angular2/src/core/annotations/di';
@Decorator({
selector: 'md-input-container input'
})
export class MdInput {
constructor() {
}
}
@Decorator({
selector: 'md-input-container'
})
export class MdInputContainer {
}

View File

@ -0,0 +1,257 @@
@import "../../core/style/variables";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
@mixin input-placeholder-color($color) {
&::-webkit-input-placeholder,
&::-moz-placeholder, /* Firefox 19+ */
&:-moz-placeholder, /* Firefox 18- */
&:-ms-input-placeholder {
color: $color;
}
}
$input-container-padding: 2px !default;
$input-label-default-offset: 24px !default;
$input-label-default-scale: 1.0 !default;
$input-label-float-offset: 4px !default;
$input-label-float-scale: 0.75 !default;
$input-placeholder-offset: $input-label-default-offset !default;
$input-border-width-default: 1px !default;
$input-border-width-focused: 2px !default;
$input-line-height: 26px !default;
$input-padding-top: 2px !default;
$input-error-font-size: 12px !default;
$input-error-height: 24px !default;
md-input-container {
box-sizing: border-box;
display: flex;
position: relative;
flex-direction: column;
overflow-x: hidden;
padding: $input-container-padding;
padding-bottom: $input-container-padding + $input-error-height;
> md-icon {
position: absolute;
top: 5px;
left: 2px;
+ input {
margin-left: 28px * 2;
}
}
textarea,
input[type="text"],
input[type="password"],
input[type="datetime"],
input[type="datetime-local"],
input[type="date"],
input[type="month"],
input[type="time"],
input[type="week"],
input[type="number"],
input[type="email"],
input[type="url"],
input[type="search"],
input[type="tel"],
input[type="color"] {
// Remove default appearance from all input / textarea.
// `appearance` is not supported in IE, but does IE apply any txt input styling?
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
}
textarea {
resize: none;
overflow: hidden;
}
textarea.md-input {
min-height: 2 * $input-line-height + $input-border-width-focused + $input-padding-top;
-ms-flex-preferred-size: auto; // IE10
}
label:not(.md-no-float),
.md-placeholder:not(.md-select-label) {
order: 1;
pointer-events: none;
-webkit-font-smoothing: antialiased;
padding-left: $input-container-padding;
z-index: 1;
transform: translate3d(0, $input-label-default-offset, 0) scale($input-label-default-scale);
transform-origin: left top;
transition: transform $swift-ease-out-timing-function 0.25s;
}
.md-placeholder:not(.md-select-label) {
position: absolute;
top: 0;
opacity: 0;
transition-property: opacity, transform;
transform: translate3d(0, $input-placeholder-offset + $baseline-grid * 0.75, 0);
}
&.md-input-focused .md-placeholder {
opacity: 1;
transform: translate3d(0, $input-placeholder-offset, 0);
}
// Placeholder should immediately disappear when the user starts typing
&.md-input-has-value .md-placeholder {
transition: none;
opacity: 0;
}
&:not( .md-input-has-value ) input:not( :focus ) {
color: transparent;
}
/*
* The .md-input class is added to the input/textarea
*/
.md-input {
flex: 1 1 auto;
order: 2;
display: block;
background: none;
padding: $input-padding-top 2px $input-border-width-focused - $input-border-width-default;
border-width: 0 0 $input-border-width-default 0;
line-height: $input-line-height;
-ms-flex-preferred-size: $input-line-height; // IE10
border-radius: 0;
&:focus {
outline: none;
}
&:invalid {
outline: none;
box-shadow: none;
}
}
.md-char-counter {
-webkit-font-smoothing: antialiased;
position: absolute;
font-size: $input-error-font-size;
line-height: $input-error-height;
bottom: $input-container-padding;
right: $input-container-padding;
// TODO(jelbourn): animations here.
}
&.md-input-focused,
&.md-input-has-value {
label:not(.md-no-float) {
transform: translate3d(0,$input-label-float-offset,0) scale($input-label-float-scale);
}
}
// Use wide border in error state or in focused state
&.md-input-focused .md-input {
padding-bottom: 0; // Increase border width by 1px, decrease padding by 1
border-width: 0 0 $input-border-width-focused 0;
}
.md-input {
&[disabled],
[disabled] & {
background-position: 0 bottom;
// This background-size is coordinated with a linear-gradient set in input-theme.scss
// to create a dotted line under the input.
background-size: 3px 1px;
background-repeat: repeat-x;
}
}
}
// THEME
md-input-container {
.md-input {
@include input-placeholder-color(md-color($md-foreground, hint-text));
color: md-color($md-foreground, text);
border-color: md-color($md-foreground, divider);
// text-shadow: md-color($md-foreground, shadow); // what is this?
}
> md-icon {
color: md-color($md-foreground, text);
}
label,
.md-placeholder {
// text-shadow: md-color($md-foreground, shadow); // what is this?
color: md-color($md-foreground, hint-text);
}
div[ng-messages] {
color: md-color($md-warn, 500)
}
&:not(.md-input-invalid) {
&.md-input-has-value {
label {
color: md-color($md-foreground, secondary-text);
}
}
&.md-input-focused {
.md-input {
border-color: md-color($md-primary, 500);
}
label {
color: md-color($md-primary, 500);
}
md-icon {
color: md-color($md-primary, 500);
}
&.md-accent {
.md-input {
border-color: md-color($md-accent, 500);
}
label {
color: md-color($md-accent, 500);
}
}
&.md-warn {
.md-input {
border-color: md-color($md-warn, 500);
}
label {
color: md-color($md-warn, 500);
}
}
}
}
&.md-input-invalid {
.md-input {
border-color: md-color($md-warn, 500);
}
label {
color: md-color($md-warn, 500);
}
.md-char-counter {
color: md-color($md-warn, 500);
}
}
.md-input {
&[disabled],
[disabled] & {
border-bottom-color: transparent;
color: md-color($md-foreground, disabled-text);
background-image: linear-gradient(to right, md-color($md-foreground, divider) 0%, md-color($md-foreground, divider) 33%, transparent 0%);
background-image: -ms-linear-gradient(left, transparent 0%, md-color($md-foreground, divider) 100%);
}
}
}

View File

@ -0,0 +1,11 @@
<div class="md-spinner-wrapper">
<div class="md-inner">
<div class="md-gap"></div>
<div class="md-left">
<div class="md-half-circle"></div>
</div>
<div class="md-right">
<div class="md-half-circle"></div>
</div>
</div>
</div>

View File

@ -0,0 +1,14 @@
import {Component, View} from 'angular2/angular2';
import {isPresent} from 'angular2/src/facade/lang';
@Component({
selector: 'md-progress-circular'
})
@View({
templateUrl: 'angular2_material/src/components/progress-circular/progress_circular.html'
})
export class MdProgressCircular {
constructor() {
}
}

View File

@ -0,0 +1,9 @@
<style>@import "angular2_material/src/components/progress-linear/progress_linear.css";</style>
<div class="md-progress-linear-container md-ready">
<div class="md-progress-linear-dashed"></div>
<div class="md-progress-linear-bar md-progress-linear-bar1"
[style.transform]="secondaryBarTransform"></div>
<div class="md-progress-linear-bar md-progress-linear-bar2"
[style.transform]="primaryBarTransform"></div>
</div>

View File

@ -0,0 +1,98 @@
import {Component, View, Attribute, PropertySetter, onChange} from 'angular2/angular2';
import {isPresent, isBlank} from 'angular2/src/facade/lang';
import {Math} from 'angular2/src/facade/math';
@Component({
selector: 'md-progress-linear',
lifecycle: [onChange],
properties: {
'value': 'value',
'bufferValue': 'buffer-value'
}
})
@View({
templateUrl: 'angular2_material/src/components/progress-linear/progress_linear.html',
directives: []
})
export class MdProgressLinear {
/** Value for the primary bar. */
value_: number;
/** Value for the secondary bar. */
bufferValue: number;
/** The render mode for the progress bar. */
mode: string;
/** Attribute setter for aria-valuenow. */
ariaValueNowSetter: Function;
/** CSS `transform` property applied to the primary bar. */
primaryBarTransform: string;
/** CSS `transform` property applied to the secondary bar. */
secondaryBarTransform: string;
constructor(
@Attribute('md-mode') mode: string,
@PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('attr.aria-valuemin') ariaValueMinSetter: Function,
@PropertySetter('attr.aria-valuemax') ariaValueMaxSetter: Function,
@PropertySetter('attr.aria-valuenow') ariaValueNowSetter: Function) {
this.ariaValueNowSetter = ariaValueNowSetter;
this.primaryBarTransform = '';
this.secondaryBarTransform = '';
roleSetter('progressbar');
ariaValueMinSetter('0');
ariaValueMaxSetter('100');
this.mode = isPresent(mode) ? mode : Mode.DETERMINATE;
}
get value() {
return this.value_;
}
set value(v) {
if (isPresent(v)) {
this.value_ = MdProgressLinear.clamp(v);
this.ariaValueNowSetter(this.value_);
}
}
onChange(_) {
// If the mode does not use a value, or if there is no value, do nothing.
if (this.mode == Mode['QUERY'] || this.mode == Mode['INDETERMINATE'] || isBlank(this.value)) {
return;
}
this.primaryBarTransform = this.transformForValue(this.value);
// The bufferValue is only used in buffer mode.
if (this.mode == Mode['BUFFER']) {
this.secondaryBarTransform = this.transformForValue(this.bufferValue);
}
}
/** Gets the CSS `transform` property for a progress bar based on the given value (0 - 100). */
transformForValue(value) {
// TODO(jelbourn): test perf gain of caching these, since there are only 101 values.
var scale = value / 100;
var translateX = (value - 100) / 2;
return `translateX(${translateX}%) scale(${scale}, 1)`;
}
/** Clamps a value to be between 0 and 100. */
static clamp(v) {
return Math.max(0, Math.min(100, v));
}
}
/** @enum {string} Progress-linear modes. */
var Mode = {
'DETERMINATE': 'determinate',
'INDETERMINATE': 'indeterminate',
'BUFFER': 'buffer',
'QUERY': 'query'
};

View File

@ -0,0 +1,266 @@
@import "../../core/style/variables";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
$progress-linear-bar-height: 5px !default;
md-progress-linear {
display: block;
width: 100%;
height: $progress-linear-bar-height;
*, *:before {
box-sizing: border-box;
}
.md-progress-linear-container {
overflow: hidden;
position: relative;
height: $progress-linear-bar-height;
top: $progress-linear-bar-height;
transform: translate(0, 5px) scale(1, 0);
transition: all .3s linear;
}
.md-progress-linear-container.md-ready {
transform: translate(0, 0) scale(1, 1);
}
.md-progress-linear-bar {
height: $progress-linear-bar-height;
position: absolute;
width: 100%;
}
.md-progress-linear-bar1, .md-progress-linear-bar2 {
transition: all 0.2s linear;
}
&[md-mode="determinate"] {
.md-progress-linear-bar1 {
display: none;
}
}
&[md-mode="indeterminate"] {
.md-progress-linear-bar1 {
animation: indeterminate1 4s infinite linear;
}
.md-progress-linear-bar2 {
animation: indeterminate2 4s infinite linear;
}
}
&[md-mode="buffer"] {
.md-progress-linear-container {
background-color: transparent !important;
}
.md-progress-linear-dashed:before {
content: "";
display: block;
height: $progress-linear-bar-height;
width: 100%;
margin-top: 0px;
position: absolute;
background-color: transparent;
background-size: 10px 10px !important;
background-position: 0px -23px;
animation: buffer 3s infinite linear;
}
}
&[md-mode="query"] {
.md-progress-linear-bar2 {
animation: query .8s infinite cubic-bezier(0.390, 0.575, 0.565, 1.000);
}
}
}
@keyframes indeterminate1 {
0% {
transform: translateX(-25%) scale(.5, 1);
}
10% {
transform: translateX(25%) scale(.5, 1);
}
19.99% {
transform: translateX(50%) scale(0, 1);
}
20% {
transform: translateX(-37.5%) scale(.25, 1);
}
30% {
transform: translateX(37.5%) scale(.25, 1);
}
34.99% {
transform: translateX(50%) scale(0, 1);
}
36.99% {
transform: translateX(50%) scale(0, 1);
}
37% {
transform: translateX(-37.5%) scale(.25, 1);
}
47% {
transform: translateX(20%) scale(.25, 1);
}
52% {
transform: translateX(35%) scale(.05, 1);
}
55% {
transform: translateX(35%) scale(.1, 1);
}
58% {
transform: translateX(50%) scale(.1, 1);
}
61.99% {
transform: translateX(50%) scale(0, 1);
}
69.99% {
transform: translateX(50%) scale(0, 1);
}
70% {
transform: translateX(-37.5%) scale(.25, 1);
}
80% {
transform: translateX(20%) scale(.25, 1);
}
85% {
transform: translateX(35%) scale(.05, 1);
}
88% {
transform: translateX(35%) scale(.1, 1);
}
91% {
transform: translateX(50%) scale(.1, 1);
}
92.99% {
transform: translateX(50%) scale(0, 1);
}
93% {
transform: translateX(-50%) scale(0, 1);
}
100% {
transform: translateX(-25%) scale(.5, 1);
}
}
@keyframes indeterminate2 {
0% {
transform: translateX(-50%) scale(0, 1);
}
25.99%{
transform: translateX(-50%) scale(0, 1);
}
28% {
transform: translateX(-37.5%) scale(.25, 1);
}
38% {
transform: translateX(37.5%) scale(.25, 1);
}
42.99% {
transform: translateX(50%) scale(0, 1);
}
46.99% {
transform: translateX(50%) scale(0, 1);
}
49.99% {
transform: translateX(50%) scale(0, 1);
}
50% {
transform: translateX(-50%) scale(0, 1);
}
60% {
transform: translateX(-25%) scale(.5, 1);
}
70% {
transform: translateX(25%) scale(.5, 1);
}
79.99% {
transform: translateX(50%) scale(0, 1);
}
}
@keyframes query {
0% {
opacity: 1;
transform: translateX(35%) scale(.3, 1);
}
100% {
opacity: 0;
transform: translateX(-50%) scale(0, 1);
}
}
@keyframes buffer {
0% {
opacity: 1;
background-position: 0px -23px;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
background-position: -200px -23px;
}
}
// THEME
md-progress-linear {
.md-progress-linear-container {
background-color: md-color($md-primary, 100);
}
.md-progress-linear-bar {
background-color: md-color($md-primary);
}
&.md-warn .md-progress-linear-container {
background-color: md-color($md-warn, 100);
}
&.md-warn .md-progress-linear-bar {
background-color: md-color($md-warn);
}
&.md-accent .md-progress-linear-container {
background-color: md-color($md-accent, 100);
}
&.md-accent .md-progress-linear-bar {
background-color: md-color($md-accent);
}
&[md-mode=buffer] {
&.md-primary {
.md-progress-linear-bar1 {
background-color: md-color($md-primary, 100);
}
.md-progress-linear-dashed:before {
background: radial-gradient(md-color($md-primary, 100) 0%, md-color($md-primary, 100) 16%, transparent 42%);
}
}
&.md-warn {
.md-progress-linear-bar1 {
background-color: md-color($md-warn, 100);
}
.md-progress-linear-dashed:before {
background: radial-gradient(md-color($md-warn, 100) 0%, md-color($md-warn, 100) 16%, transparent 42%);
}
}
&.md-accent {
.md-progress-linear-bar1 {
background-color: md-color($md-accent, 100);
}
.md-progress-linear-dashed:before {
background: radial-gradient(md-color($md-accent, 100) 0%, md-color($md-accent, 100) 16%, transparent 42%);
}
}
}
}

View File

@ -0,0 +1,136 @@
@import "../../core/style/variables";
@import "../../core/style/shadows";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
$radio-width: 16px !default;
$radio-height: $radio-width !default;
md-radio-button {
display: block;
margin: 15px;
white-space: nowrap;
cursor: pointer;
}
md-radio-group {
border: 1px dotted transparent;
display: block;
outline: none;
}
.md-radio-container {
box-sizing: border-box;
position: relative;
top: 4px;
display: inline-block;
width: $radio-width;
height: $radio-width;
cursor: pointer;
}
.md-radio-off {
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: $radio-width;
height: $radio-width;
border: solid 2px;
border-radius: 50%;
transition: border-color ease 0.28s;
}
.md-radio-on {
box-sizing: border-box;
position: absolute;
top: 0;
left: 0;
width: $radio-width;
height: $radio-width;
border-radius: 50%;
transition: transform ease 0.28s;
transform: scale(0);
.md-radio-checked & {
transform: scale(0.55);
}
}
// This is the style applied to the content (included via <content>). If we could rely on shadow
// DOM always being present, this would use the ::content psuedo-class.
.md-radio-label {
position: relative;
display: inline-block;
margin-left: 10px;
margin-right: 10px;
vertical-align: middle;
white-space: normal;
// pointer-events: none; ???
width: auto;
}
// THEME
.md-radio-off {
border-color: md-color($md-foreground, icon);
}
.md-radio-on {
background-color: md-color($md-accent, 0.87);
}
.md-radio-checked .md-radio-off {
border-color: md-color($md-accent, 0.87);
}
// TODO
.md-radio-checked .md-ink-ripple { }
.md-radio-container .md-ripple { }
p
md-radio-group:not([disabled]) md-radio:not([disabled]) {
&.md-primary {
.md-radio-on {
background-color: md-color($md-primary, 0.87);
}
&.md-radio-checked .md-radio-off {
border-color: md-color($md-primary, 0.87);
}
&.md-radio-checked .md-ink-ripple {
color: md-color($md-primary, 0.87);
}
.md-radio-container .md-ripple {
color: md-color($md-primary, 600);
}
}
&.md-warn {
.md-radio-on {
background-color: md-color($md-warn, 0.87);
}
&.md-radio-checked .md-radio-off {
border-color: md-color($md-warn, 0.87);
}
&.md-radio-checked .md-ink-ripple {
color: md-color($md-warn, 0.87);
}
.md-radio-container .md-ripple {
color: md-color($md-warn, 600);
}
}
}
md-radio-button[disabled],
md-radio-group[disabled] md-radio-button {
.md-radio-container .md-radio-off,
.md-radio-container .md-radio-on {
border-color: md-color($md-foreground, disabled);
}
}

View File

@ -0,0 +1,5 @@
md-radio-group {
border: 1px dotted transparent;
display: block;
outline: none;
}

View File

@ -0,0 +1,18 @@
<style>@import "angular2_material/src/components/radio/radio-button.css";</style>
<!-- TODO(jelbourn): render the radio on either side of the content -->
<label role="radio"
[class.md-radio-checked]="checked"
(^click)="select($event)" >
<!-- The actual `radio` part of the control. -->
<div class="md-radio-container">
<div class="md-radio-off"></div>
<div class="md-radio-on"></div>
</div>
<!-- The label for radio control. -->
<div class="md-radio-label">
<content></content>
</div>
</label>

View File

@ -0,0 +1,341 @@
import {Component, View, Parent, Ancestor, Attribute, PropertySetter,
EventEmitter} from 'angular2/angular2';
import {Optional} from 'angular2/src/di/annotations';
import {MdRadioDispatcher} from 'angular2_material/src/components/radio/radio_dispatcher'
import {MdTheme} from 'angular2_material/src/core/theme'
import {onChange} from 'angular2/src/core/annotations/annotations';
import {isPresent, StringWrapper} from 'angular2/src/facade/lang';
// import {KeyCodes} from 'angular2_material/src/core/constants'
import {Math} from 'angular2/src/facade/math';
import {ListWrapper} from 'angular2/src/facade/collection';
// TODO(jelbourn): Behaviors to test
// Disabled radio don't select
// Disabled radios don't propagate click event
// Radios are disabled by parent group
// Radios set default tab index iff not in parent group
// Radios are unique-select
// Radio updates parent group's value
// Change to parent group's value updates the selected child radio
// Radio name is pulled on parent group
// Radio group changes on arrow keys
// Radio group skips disabled radios on arrow keys
var _uniqueIdCounter:number = 0;
@Component({
selector: 'md-radio-button',
lifecycle: [onChange],
properties: {
'id': 'id',
'name': 'name',
'value': 'value',
'checked': 'checked',
'disabled': 'disabled'
},
hostListeners: {
'keydown': 'onKeydown($event)'
}
})
@View({
templateUrl: 'angular2_material/src/components/radio/radio_button.html',
directives: []
})
export class MdRadioButton {
/** Whether this radio is checked. */
checked_: boolean;
/** Whether the radio is disabled. */
disabled_: boolean;
/** The unique ID for the radio button. */
id: string;
/** Analog to HTML 'name' attribute used to group radios for unique selection. */
name: string;
/** Value assigned to this radio. Used to assign the value to the parent MdRadioGroup. */
value: any;
/** The parent radio group. May or may not be present. */
radioGroup: MdRadioGroup;
/** Dispatcher for coordinating radio unique-selection by name. */
radioDispatcher: MdRadioDispatcher;
/** Setter for `aria-checked` attribute. */
ariaCheckedSetter: Function;
/** Setter for `aria-disabled` attribute. */
ariaDisabledSetter: Function;
constructor(
@Optional() @Parent() radioGroup: MdRadioGroup,
@Attribute('id') id: string,
@Attribute('tabindex') tabindex: string,
@PropertySetter('id') idSetter: Function,
@PropertySetter('tabindex') tabindexSetter: Function,
@PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function,
radioDispatcher: MdRadioDispatcher) {
// Assertions. Ideally these should be stripped out by the compiler.
// TODO(jelbourn): Assert that there's no name binding AND a parent radio group.
this.radioGroup = radioGroup;
this.radioDispatcher = radioDispatcher;
this.ariaCheckedSetter = ariaCheckedSetter;
this.ariaDisabledSetter = ariaDisabledSetter;
this.value = null;
roleSetter('radio');
this.checked = false;
this.id = isPresent(id) ? id : `md-radio-${_uniqueIdCounter++}`;
idSetter(this.id);
// Whenever a radio button with the same name is checked, uncheck this radio button.
radioDispatcher.listen((name) => {
if (name == this.name) {
this.checked = false;
}
});
// When this radio-button is inside of a radio-group, the group determines the name.
if (isPresent(radioGroup)) {
this.name = radioGroup.getName();
this.radioGroup.register(this);
}
// If the user has not set a tabindex, default to zero (in the normal document flow).
if (!isPresent(radioGroup)) {
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
}
}
/** Change handler invoked when bindings are resolved or when bindings have changed. */
onChange(_) {
if (isPresent(this.radioGroup)) {
this.name = this.radioGroup.getName();
}
}
/** Whether this radio button is disabled, taking the parent group into account. */
isDisabled(): boolean {
// Here, this.disabled may be true/false as the result of a binding, may be the empty string
// if the user just adds a `disabled` attribute with no value, or may be absent completely.
// TODO(jelbourn): If someone sets `disabled="disabled"`, will this work in dart?
return this.disabled ||
(isPresent(this.disabled) && StringWrapper.equals(this.disabled, '')) ||
(isPresent(this.radioGroup) && this.radioGroup.disabled);
}
get checked() {
return this.checked_;
}
set checked(value) {
this.checked_ = value;
this.ariaCheckedSetter(value);
}
get disabled() {
return this.disabled_;
}
set disabled(value) {
this.disabled_ = isPresent(value) && value !== false;
this.ariaDisabledSetter(this.disabled_);
}
/** Select this radio button. */
select(event: Event) {
if (this.isDisabled()) {
event.stopPropagation();
return;
}
// Notifiy all radio buttons with the same name to un-check.
this.radioDispatcher.notify(this.name);
this.checked = true;
if (this.radioGroup) {
this.radioGroup.updateValue(this.value, this.id);
}
}
/** Handles pressing the space key to select this focused radio button. */
onKeydown(event: KeyboardEvent) {
if (event.keyCode == KeyCodes.SPACE) {
event.preventDefault();
this.select(event);
}
}
}
@Component({
selector: 'md-radio-group',
lifecycle: [onChange],
properties: {
'disabled': 'disabled',
'value': 'value'
},
hostListeners: {
'keydown': 'onKeydown($event)'
}
})
@View({
templateUrl: 'angular2_material/src/components/radio/radio_group.html'
})
export class MdRadioGroup {
/** The selected value for the radio group. The value comes from the options. */
value: any;
/** The HTML name attribute applied to radio buttons in this group. */
name_: string;
/** Dispatcher for coordinating radio unique-selection by name. */
radioDispatcher: MdRadioDispatcher;
/** List of child radio buttons. */
radios_: List<MdRadioButton>;
changeEmitter: Function;
ariaActiveDescendantSetter: Function;
ariaDisabledSetter: Function;
disabled_: boolean;
/** The ID of the selected radio button. */
selectedRadioId: string;
constructor(
@Attribute('tabindex') tabindex: string,
@Attribute('disabled') disabled: string,
@PropertySetter('tabindex') tabindexSetter: Function,
@PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function,
@PropertySetter('attr.aria-activedescendant') ariaActiveDescendantSetter: Function,
@EventEmitter('change') changeEmitter: Function,
radioDispatcher: MdRadioDispatcher) {
this.name_ = `md-radio-group-${_uniqueIdCounter++}`;
this.radios_ = [];
this.changeEmitter = changeEmitter;
this.ariaActiveDescendantSetter = ariaActiveDescendantSetter;
this.ariaDisabledSetter = ariaDisabledSetter;
this.radioDispatcher = radioDispatcher;
this.selectedRadioId = '';
this.disabled_ = false;
roleSetter('radiogroup');
// The simple presence of the `disabled` attribute dictates disabled state.
this.disabled = isPresent(disabled);
// If the user has not set a tabindex, default to zero (in the normal document flow).
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
}
/** Gets the name of this group, as to be applied in the HTML 'name' attribute. */
getName(): string {
return this.name_;
}
get disabled() {
return this.disabled_;
}
set disabled(value) {
this.disabled_ = isPresent(value) && value !== false;
this.ariaDisabledSetter(this.disabled_);
}
/** Change handler invoked when bindings are resolved or when bindings have changed. */
onChange(_) {
// If the component has a disabled attribute with no value, it will set disabled = ''.
this.disabled = isPresent(this.disabled) && this.disabled !== false;
// If the value of this radio-group has been set or changed, we have to look through the
// child radio buttons and select the one that has a corresponding value (if any).
if (isPresent(this.value) && this.value != '') {
this.radioDispatcher.notify(this.name_);
ListWrapper.forEach(this.radios_, (radio) => {
if (radio.value == this.value) {
radio.checked = true;
this.selectedRadioId = radio.id;
this.ariaActiveDescendantSetter(radio.id);
}
});
}
}
/** Update the value of this radio group from a child md-radio being selected. */
updateValue(value: any, id: string) {
this.value = value;
this.selectedRadioId = id;
this.ariaActiveDescendantSetter(id);
this.changeEmitter();
}
/** Registers a child radio button with this group. */
register(radio: MdRadioButton) {
ListWrapper.push(this.radios_, radio);
}
/** Handles up and down arrow key presses to change the selected child radio. */
onKeydown(event: KeyboardEvent) {
if (this.disabled) {
return;
}
switch (event.keyCode) {
//case KeyCodes.UP:
case 38:
this.stepSelectedRadio(-1);
event.preventDefault();
break;
//case KeyCodes.DOWN:
case 40:
this.stepSelectedRadio(1);
event.preventDefault();
break;
}
}
// TODO(jelbourn): Replace this with a findIndex method in the collections facade.
getSelectedRadioIndex(): number {
for (var i = 0; i < this.radios_.length; i++) {
if (this.radios_[i].id == this.selectedRadioId) {
return i;
}
}
return -1;
}
/** Steps the selected radio based on the given step value (usually either +1 or -1). */
stepSelectedRadio(step) {
var index = this.getSelectedRadioIndex() + step;
if (index < 0 || index >= this.radios_.length) {
return;
}
var radio = this.radios_[index];
// If the next radio is line is disabled, skip it (maintaining direction).
if (radio.disabled) {
this.stepSelectedRadio(step + (step < 0 ? -1 : 1));
return;
}
this.radioDispatcher.notify(this.name_);
radio.checked = true;
this.value = radio.value;
this.selectedRadioId = radio.id;
this.ariaActiveDescendantSetter(radio.id);
}
}

View File

@ -0,0 +1,24 @@
import {ListWrapper} from 'angular2/src/facade/collection';
/**
* Class for radio buttons to coordinate unique selection based on name.
* Indended to be consumed as an Angular service.
*/
export class MdRadioDispatcher {
// TODO(jelbourn): Change this to TypeScript syntax when supported.
listeners_: Array<Function>;
constructor() {
this.listeners_ = [];
}
/** Notify other nadio buttons that selection for the given name has been set. */
notify(name: string) {
ListWrapper.forEach(this.listeners_, (f) => f(name));
}
/** Listen for future changes to radio button selection. */
listen(listener) {
ListWrapper.push(this.listeners_, listener);
}
}

View File

@ -0,0 +1,2 @@
<style>@import "angular2_material/src/components/radio/radio-group.css";</style>
<content></content>

View File

@ -0,0 +1,11 @@
<style>@import "angular2_material/src/components/switcher/switch.css";</style>
<div (^click)="toggle($event)">
<div class="md-switch-container">
<div class="md-switch-bar"></div>
<div class="md-switch-thumb-container">
<div class="md-switch-thumb"></div>
</div>
</div>
<div class="md-switch-label"><content></content></div>
</div>

View File

@ -0,0 +1,80 @@
import {Component, View, Attribute, PropertySetter} from 'angular2/angular2';
import {isPresent} from 'angular2/src/facade/lang';
import {KeyCodes} from 'angular2_material/src/core/constants'
// TODO(jelbourn): without gesture support, this is identical to MdCheckbox.
@Component({
selector: 'md-switch',
properties: {
'checked': 'checked',
'disabled': 'disabled'
},
hostListeners: {
'keydown': 'onKeydown($event)'
}
})
@View({
templateUrl: 'angular2_material/src/components/switcher/switch.html',
directives: []
})
export class MdSwitch {
/** Whether this switch is checked. */
checked_: boolean;
/** Setter for `aria-checked` attribute. */
ariaCheckedSetter: Function;
/** Setter for `aria-disabled` attribute. */
ariaDisabledSetter: Function;
constructor(
@Attribute('tabindex') tabindex: string,
@PropertySetter('tabindex') tabindexSetter: Function,
@PropertySetter('attr.role') roleSetter: Function,
@PropertySetter('attr.aria-checked') ariaCheckedSetter: Function,
@PropertySetter('attr.aria-disabled') ariaDisabledSetter: Function) {
this.ariaCheckedSetter = ariaCheckedSetter;
this.ariaDisabledSetter = ariaDisabledSetter;
roleSetter('checkbox');
this.checked = false;
tabindexSetter(isPresent(tabindex) ? tabindex : '0');
}
get checked() {
return this.checked_;
}
set checked(value) {
this.checked_ = value;
this.ariaCheckedSetter(value);
}
get disabled() {
return this.disabled_;
}
set disabled(value) {
this.disabled_ = isPresent(value) && value !== false;
this.ariaDisabledSetter(this.disabled_);
}
onKeydown(event: KeyboardEvent) {
if (event.keyCode == KeyCodes.SPACE) {
event.preventDefault();
this.toggle(event);
}
}
toggle(event) {
if (this.disabled) {
event.stopPropagation();
return;
}
this.checked = !this.checked;
this.ariaCheckedSetter(this.checked);
}
}

View File

@ -0,0 +1,172 @@
@import "../../core/style/variables";
// TODO(jelbourn): This goes away.
@import "../../core/style/default-theme";
$switch-width: 36px !default;
$switch-height: $baseline-grid * 3 !default;
$switch-bar-height: 14px !default;
$switch-thumb-size: 20px !default;
md-switch {
display: flex;
align-items: center;
margin: 15px;
white-space: nowrap;
cursor: pointer;
outline: none;
user-select: none;
* {
box-sizing: border-box;
}
.md-switch-container {
cursor: grab;
width: $switch-width;
height: $switch-height;
position: relative;
user-select: none;
margin-right: 8px;
}
// If the user moves his mouse off the switch, stil display grabbing cursor
&:not([disabled]) {
.md-switch-dragging,
&.md-switch-dragging .md-switch-container {
cursor: grabbing;
}
}
.md-switch-label {
border: 0 transparent;
}
.md-switch-bar {
left: 1px;
width: $switch-width - 2px;
top: $switch-height / 2 - $switch-bar-height / 2;
height: $switch-bar-height;
border-radius: 8px;
position: absolute;
}
.md-switch-thumb-container {
top: $switch-height / 2 - $switch-thumb-size / 2;
left: 0;
width: $switch-width - $switch-thumb-size;
position: absolute;
transform: translate3d(0,0,0);
z-index: 1;
}
&[aria-checked="true"] .md-switch-thumb-container {
transform: translate3d(100%, 0, 0);
}
.md-switch-thumb {
position: absolute;
margin: 0;
left: 0;
top: 0;
outline: none;
height: $switch-thumb-size;
width: $switch-thumb-size;
border-radius: 50%;
box-shadow: $whiteframe-shadow-z1;
// todo
.md-ripple-container {
position: absolute;
display: block;
width: auto;
height: auto;
left: -$switch-thumb-size;
top: -$switch-thumb-size;
right: -$switch-thumb-size;
bottom: -$switch-thumb-size;
}
}
&:not(.md-switch-dragging) {
.md-switch-bar,
.md-switch-thumb-container,
.md-switch-thumb {
transition: $swift-ease-in-out;
transition-property: transform, background-color;
}
.md-switch-bar,
.md-switch-thumb {
transition-delay: 0.05s;
}
}
}
// TODO(jelbourn): Why are these not defined in terms of the theme?
@media screen and (-ms-high-contrast: active) {
md-switch .md-switch-bar {
background-color: #666;
}
md-switch[aria-checked="true"] .md-switch-bar {
background-color: #9E9E9E;
}
md-switch.md-default-theme .md-switch-thumb {
background-color: #fff;
}
}
// THEME
md-switch {
.md-switch-thumb {
background-color: md-color($md-background, 50);
}
.md-switch-bar {
background-color: md-color($md-background, 500);
}
&[aria-checked="true"] {
.md-switch-thumb {
background-color: md-color($md-accent);
}
.md-switch-bar {
background-color: md-color($md-accent, 0.5);
}
&.md-primary {
.md-switch-thumb {
background-color: md-color($md-primary);
}
.md-switch-bar {
background-color: md-color($md-primary, 0.5);
}
}
&.md-warn {
.md-switch-thumb {
background-color: md-color($md-warn);
}
.md-switch-bar {
background-color: md-color($md-warn, 0.5);
}
}
}
&[disabled] {
.md-switch-thumb {
background-color: md-color($md-background, 400);
}
.md-switch-bar {
background-color: md-color($md-foreground, divider);
}
}
&:focus {
.md-switch-label:not(:empty) {
border: 1px dotted md-color($md-foreground, text);
}
}
}

View File

@ -0,0 +1,8 @@
// TODO: this is not how TS&Dart enums are defined and it definitely won't
// work in Dart. Switch to proper enums when we support them.
//export var KeyCodes = {
// SPACE: 32,
// UP: 38,
// DOWN: 40
//};

View File

@ -0,0 +1,13 @@
@import 'theme-functions';
@import 'palette';
// Person creating a theme writes variables like this:
$md-is-dark-theme: false;
$md-primary: md-palette($md-indigo, 500, 100, 700, $md-contrast-palettes);
$md-accent: md-palette($md-red, A200, A100, A400, $md-contrast-palettes);
$md-background: md-palette($md-grey, 500, 300, 600, $md-contrast-palettes);
$md-warn: md-palette($md-red, 500, 300, 800, $md-contrast-palettes);
$md-foreground: if($md-is-dark-theme, $md-dark-theme-foreground, $md-light-theme-foreground);

View File

@ -0,0 +1,666 @@
// Core color palettes.
$md-red: (
50: #ffebee,
100: #ffcdd2,
200: #ef9a9a,
300: #e57373,
400: #ef5350,
500: #f44336,
600: #e53935,
700: #d32f2f,
800: #c62828,
900: #b71c1c,
A100: #ff8a80,
A200: #ff5252,
A400: #ff1744,
A700: #d50000,
);
$md-pink: (
50: #fce4ec,
100: #f8bbd0,
200: #f48fb1,
300: #f06292,
400: #ec407a,
500: #e91e63,
600: #d81b60,
700: #c2185b,
800: #ad1457,
900: #880e4f,
A100: #ff80ab,
A200: #ff4081,
A400: #f50057,
A700: #c51162,
);
$md-purple: (
50: #f3e5f5,
100: #e1bee7,
200: #ce93d8,
300: #ba68c8,
400: #ab47bc,
500: #9c27b0,
600: #8e24aa,
700: #7b1fa2,
800: #6a1b9a,
900: #4a148c,
A100: #ea80fc,
A200: #e040fb,
A400: #d500f9,
A700: #aa00ff,
);
$md-deep-purple: (
50: #ede7f6,
100: #d1c4e9,
200: #b39ddb,
300: #9575cd,
400: #7e57c2,
500: #673ab7,
600: #5e35b1,
700: #512da8,
800: #4527a0,
900: #311b92,
A100: #b388ff,
A200: #7c4dff,
A400: #651fff,
A700: #6200ea,
);
$md-indigo: (
50: #e8eaf6,
100: #c5cae9,
200: #9fa8da,
300: #7986cb,
400: #5c6bc0,
500: #3f51b5,
600: #3949ab,
700: #303f9f,
800: #283593,
900: #1a237e,
A100: #8c9eff,
A200: #536dfe,
A400: #3d5afe,
A700: #304ffe,
);
$md-blue: (
50: #e3f2fd,
100: #bbdefb,
200: #90caf9,
300: #64b5f6,
400: #42a5f5,
500: #2196f3,
600: #1e88e5,
700: #1976d2,
800: #1565c0,
900: #0d47a1,
A100: #82b1ff,
A200: #448aff,
A400: #2979ff,
A700: #2962ff,
);
$md-light-blue: (
50: #e1f5fe,
100: #b3e5fc,
200: #81d4fa,
300: #4fc3f7,
400: #29b6f6,
500: #03a9f4,
600: #039be5,
700: #0288d1,
800: #0277bd,
900: #01579b,
A100: #80d8ff,
A200: #40c4ff,
A400: #00b0ff,
A700: #0091ea,
);
$md-cyan: (
50: #e0f7fa,
100: #b2ebf2,
200: #80deea,
300: #4dd0e1,
400: #26c6da,
500: #00bcd4,
600: #00acc1,
700: #0097a7,
800: #00838f,
900: #006064,
A100: #84ffff,
A200: #18ffff,
A400: #00e5ff,
A700: #00b8d4,
);
$md-teal: (
50: #e0f2f1,
100: #b2dfdb,
200: #80cbc4,
300: #4db6ac,
400: #26a69a,
500: #009688,
600: #00897b,
700: #00796b,
800: #00695c,
900: #004d40,
A100: #a7ffeb,
A200: #64ffda,
A400: #1de9b6,
A700: #00bfa5,
);
$md-green: (
50: #e8f5e9,
100: #c8e6c9,
200: #a5d6a7,
300: #81c784,
400: #66bb6a,
500: #4caf50,
600: #43a047,
700: #388e3c,
800: #2e7d32,
900: #1b5e20,
A100: #b9f6ca,
A200: #69f0ae,
A400: #00e676,
A700: #00c853,
);
$md-light-green: (
50: #f1f8e9,
100: #dcedc8,
200: #c5e1a5,
300: #aed581,
400: #9ccc65,
500: #8bc34a,
600: #7cb342,
700: #689f38,
800: #558b2f,
900: #33691e,
A100: #ccff90,
A200: #b2ff59,
A400: #76ff03,
A700: #64dd17,
);
$md-lime: (
50: #f9fbe7,
100: #f0f4c3,
200: #e6ee9c,
300: #dce775,
400: #d4e157,
500: #cddc39,
600: #c0ca33,
700: #afb42b,
800: #9e9d24,
900: #827717,
A100: #f4ff81,
A200: #eeff41,
A400: #c6ff00,
A700: #aeea00,
);
$md-yellow: (
50: #fffde7,
100: #fff9c4,
200: #fff59d,
300: #fff176,
400: #ffee58,
500: #ffeb3b,
600: #fdd835,
700: #fbc02d,
800: #f9a825,
900: #f57f17,
A100: #ffff8d,
A200: #ffff00,
A400: #ffea00,
A700: #ffd600,
);
$md-amber: (
50: #fff8e1,
100: #ffecb3,
200: #ffe082,
300: #ffd54f,
400: #ffca28,
500: #ffc107,
600: #ffb300,
700: #ffa000,
800: #ff8f00,
900: #ff6f00,
A100: #ffe57f,
A200: #ffd740,
A400: #ffc400,
A700: #ffab00,
);
$md-orange: (
50: #fff3e0,
100: #ffe0b2,
200: #ffcc80,
300: #ffb74d,
400: #ffa726,
500: #ff9800,
600: #fb8c00,
700: #f57c00,
800: #ef6c00,
900: #e65100,
A100: #ffd180,
A200: #ffab40,
A400: #ff9100,
A700: #ff6d00,
);
$md-deep-orange: (
50: #fbe9e7,
100: #ffccbc,
200: #ffab91,
300: #ff8a65,
400: #ff7043,
500: #ff5722,
600: #f4511e,
700: #e64a19,
800: #d84315,
900: #bf360c,
A100: #ff9e80,
A200: #ff6e40,
A400: #ff3d00,
A700: #dd2c00,
);
$md-brown: (
50: #efebe9,
100: #d7ccc8,
200: #bcaaa4,
300: #a1887f,
400: #8d6e63,
500: #795548,
600: #6d4c41,
700: #5d4037,
800: #4e342e,
900: #3e2723,
A100: #d7ccc8,
A200: #bcaaa4,
A400: #8d6e63,
A700: #5d4037,
);
$md-grey: (
0: #ffffff,
50: #fafafa,
100: #f5f5f5,
200: #eeeeee,
300: #e0e0e0,
400: #bdbdbd,
500: #9e9e9e,
600: #757575,
700: #616161,
800: #424242,
900: #212121,
1000: #000000,
A100: #ffffff,
A200: #eeeeee,
A400: #bdbdbd,
A700: #616161,
);
$md-blue-grey: (
50: #eceff1,
100: #cfd8dc,
200: #b0bec5,
300: #90a4ae,
400: #78909c,
500: #607d8b,
600: #546e7a,
700: #455a64,
800: #37474f,
900: #263238,
A100: #cfd8dc,
A200: #b0bec5,
A400: #78909c,
A700: #455a64,
);
$md-light-theme-foreground: (
divider: rgba(black, 0.12),
dividers: rgba(black, 0.12),
disabled: rgba(black, 0.26),
disabled-text: rgba(black, 0.26),
hint-text: rgba(black, 0.26),
secondary-text: rgba(black, 0.54),
icon: rgba(black, 0.54),
icons: rgba(black, 0.54),
text: rgba(black, 0.87)
);
$md-dark-theme-foreground: (
divider: rgba(white, 0.12),
dividers: rgba(white, 0.12),
disabled: rgba(white, 0.30),
disabled-text: rgba(white, 0.30),
hint-text: rgba(white, 0.30),
secondary-text: rgba(white, 0.70),
icon: white,
icons: white,
text: white
);
// Contrast colors. These are hard-coded because it is too difficult to calculate them.
// These contrast colors are pulled from the public Material Design spec swatches. While the
// contrast colors in the spec are not perscriptive, we will use them for convenience.
$black-87-opacity: rgba(black, 0.870588);
$white-87-opacity: rgba(white, 0.870588);
$md-contrast-palettes: (
$md-red: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: white,
A700: white,
),
$md-pink: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: white,
A700: white,
),
$md-purple: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: white,
400: white,
500: $white-87-opacity,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: white,
A700: white,
),
$md-deep-purple: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: white,
400: white,
500: $white-87-opacity,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: $white-87-opacity,
A700: $white-87-opacity,
),
$md-indigo: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: white,
400: white,
500: $white-87-opacity,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: white,
A700: $white-87-opacity,
),
$md-blue: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: white,
A400: white,
A700: white,
),
$md-light-blue: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: white,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: white,
),
$md-cyan: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: white,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-teal: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-green: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-light-green: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $black-87-opacity,
700: $black-87-opacity,
800: white,
900: white,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-lime: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $black-87-opacity,
700: $black-87-opacity,
800: $black-87-opacity,
900: white,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-yellow: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $black-87-opacity,
700: $black-87-opacity,
800: $black-87-opacity,
900: $black-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-amber: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $black-87-opacity,
700: $black-87-opacity,
800: $black-87-opacity,
900: $black-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $black-87-opacity,
),
$md-orange: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $black-87-opacity,
700: $black-87-opacity,
800: white,
900: white,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: black,
),
$md-deep-orange: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: white,
600: white,
700: white,
800: white,
900: white,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: white,
A700: white,
),
$md-brown: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: white,
400: white,
500: $white-87-opacity,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: white,
A700: $white-87-opacity,
),
$md-grey: (
0: $black-87-opacity,
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: $black-87-opacity,
500: $black-87-opacity,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
1000: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: $black-87-opacity,
A700: $white-87-opacity,
),
$md-blue-grey: (
50: $black-87-opacity,
100: $black-87-opacity,
200: $black-87-opacity,
300: $black-87-opacity,
400: white,
500: white,
600: $white-87-opacity,
700: $white-87-opacity,
800: $white-87-opacity,
900: $white-87-opacity,
A100: $black-87-opacity,
A200: $black-87-opacity,
A400: white,
A700: $white-87-opacity,
),
);

View File

@ -0,0 +1,9 @@
// Elements can have an "elevation" from 1 to 5, signified by shadows.
// See http://google.com/design/spec/what-is-material/objects-in-3d-space.html
.md-shadow-bottom-z-1 {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
}
.md-shadow-bottom-z-2 {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4);
}

View File

@ -0,0 +1,56 @@
// ** Two main functions for users **
// md-palette: used for defining your theme in terms of Material hues.
// md-color: apply colors to components from the palette. Consumes the output of md-palette
// For a given hue in a palette, return the contrast color from the map of contrast palettes.
@function md-contrast($color-map, $hue, $contrast-color-map) {
@return map-get(map-get($contrast-color-map, $color-map), $hue);
}
// Creates a map of hues to colors for a theme.
// $color-map
// $primary
// $lighter
// $darker
@function md-palette($color-map, $primary, $lighter, $darker, $contrast-color-map) {
$result: map_merge($color-map, (
default: map-get($color-map, $primary),
lighter: map-get($color-map, $lighter),
darker: map-get($color-map, $darker),
default-contrast: md-contrast($color-map, $primary, $contrast-color-map),
lighter-contrast: md-contrast($color-map, $lighter, $contrast-color-map),
darker-contrast: md-contrast($color-map, $darker, $contrast-color-map)
));
// For each hue in the palette, add a "-contrast" color to the map.
@each $hue, $color in $color-map {
$result: map_merge($result, (
"#{$hue}-contrast": md-contrast($color-map, $hue, $contrast-color-map)
))
}
@return $result;
}
// Gets a color for a material design component.
// $color-map: a map of {key: color}.
// $hue-key: key used to lookup the color in $colorMap. Defaults to 'default'
// If $hue-key is a number between 0 and 1, it will be treated as $opacity.
// $opacity: the opacity to apply to the color.
@function md-color($color-map, $hue-key: default, $opacity: 1) {
// If hueKey is a number between zero and one, then it actually contains an
// opacity value, so recall this function with the default hue and that given opacity.
@if type-of($hue-key) == number and $hue-key >= 0 and $hue-key <= 1 {
@return md-color($color-map, default, $hue-key)
}
$color: map-get($color-map, $hue-key);
$opacity: if(opacity($color) < 1, opacity($color), $opacity);
@return rgba($color, $opacity);
}

View File

@ -0,0 +1,56 @@
// Font
$font-family: RobotoDraft, Roboto, 'Helvetica Neue', sans-serif !default;
// Layout
$baseline-grid: 8px !default;
$layout-breakpoint-sm: 600px !default;
$layout-breakpoint-md: 960px !default;
$layout-breakpoint-lg: 1200px !default;
$layout-gutter-width: ($baseline-grid * 2) !default;
// App bar variables
$app-bar-height: 64px;
// Toast
$toast-height: $baseline-grid * 3 !default;
$toast-margin: $baseline-grid * 1 !default;
// Whiteframes
$whiteframe-shadow-z1: 0px 2px 5px 0 rgba(0,0,0,0.26) !default;
$whiteframe-zindex-z1: 1 !default;
$whiteframe-shadow-z2: 0px 8px 17px rgba(0,0,0,0.2) !default;
$whiteframe-zindex-z2: 2 !default;
$whiteframe-shadow-z3: 0px 17px 50px rgba(0,0,0,0.19) !default;
$whiteframe-zindex-z3: 3 !default;
$whiteframe-shadow-z4: 0px 16px 28px 0 rgba(0,0,0,0.22) !default;
$whiteframe-zindex-z4: 4 !default;
$whiteframe-shadow-z5: 0px 27px 24px 0 rgba(0,0,0,0.2) !default;
$whiteframe-zindex-z5: 5 !default;
// Z-indexes
$z-index-tooltip: 100 !default;
$z-index-dialog: 80 !default;
$z-index-toast: 90 !default;
$z-index-bottom-sheet: 70 !default;
$z-index-sidenav: 60 !default;
$z-index-backdrop: 50 !default;
$z-index-fab: 20 !default;
// Easing Curves
$swift-ease-out-duration: 0.4s !default;
$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default;
$swift-ease-out: all $swift-ease-out-duration $swift-ease-out-timing-function !default;
$swift-ease-in-duration: 0.3s !default;
$swift-ease-in-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2) !default;
$swift-ease-in: all $swift-ease-in-duration $swift-ease-in-timing-function !default;
$swift-ease-in-out-duration: 0.5s !default;
$swift-ease-in-out-timing-function: cubic-bezier(0.35, 0, 0.25, 1) !default;
$swift-ease-in-out: all $swift-ease-in-out-duration $swift-ease-in-out-timing-function !default;

View File

@ -0,0 +1,12 @@
import {Decorator} from 'angular2/angular2';
@Decorator({
selector: '[md-theme]'
})
export class MdTheme {
color: string;
constructor() {
this.color = 'sky-blue'
}
}

View File

@ -3,10 +3,13 @@ environment:
sdk: '>=1.4.0'
dependencies:
angular2: '^<%= packageJson.version %>'
angular2_material: '^<%= packageJson.version %>'
browser: '>=0.10.0 <0.11.0'
dependency_overrides:
angular2:
path: ../angular2
angular2_material:
path: ../angular2_material
dev_dependencies:
guinness: ">=0.1.17 <0.2.0"
benchpress:
@ -16,7 +19,7 @@ transformers:
entry_points: web/src/hello_world/index_common.dart
reflection_entry_points: web/src/hello_world/index.dart
- $dart2js:
minify: true
commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]
minify: false
#commandLineOptions: [--trust-type-annotations, --trust-primitives, --dump-info]
#commandLineOptions: [--trust-type-annotations, --dump-info]
#commandLineOptions: [--dump-info]
commandLineOptions: [--dump-info]

View File

@ -0,0 +1,81 @@
<style>
section {
background: #f7f7f7;
border-radius: 3px;
text-align: center;
margin: 1em;
position: relative !important;
padding-bottom: 10px;
}
section [md-button]:not(.md-fab) {
min-width: 10em;
}
section [md-button] {
display: inline-block;
margin: 1em;
line-height: 25px;
}
.label {
position: absolute;
bottom: 5px;
left: 7px;
color: #ccc;
font-size: 14px;
}
</style>
<h1>Button demo</h1>
<p>
You just clicked: <span>{{previousClick}}</span>
</p>
<section>
<form (^submit)="submit('form submit')">
<button md-button>SUBMIT</button>
<button>Native button</button>
</form>
<span class="label">form submit</span>
</section>
<section>
<span class="label">Regular button</span>
<button md-button (^click)="click('button')">BUTTON</button>
<button md-button class="md-primary" (^click)="click('primary')">PRIMARY</button>
<button md-button disabled="disabled" (^click)="click('disabled')">DISABLED</button>
<button md-button class="md-accent" (^click)="click('accent')">ACCENT</button>
<button md-button class="md-warn" (^click)="click('warn')">WARN</button>
</section>
<section>
<span class="label">Raised button</span>
<button md-button class="md-raised" (^click)="click('raised')">BUTTON</button>
<button md-button class="md-raised md-primary" (^click)="click('raised primary')">PRIMARY</button>
<button md-button class="md-raised" disabled="disabled" (^click)="click('raised disabled')">DISABLED</button>
<button md-button class="md-raised md-accent" (^click)="click('raised accent')">ACCENT</button>
<button md-button class="md-raised md-warn" (^click)="click('raised warn')">WARN</button>
</section>
<section>
<span class="label">Fab button</span>
<button md-button class="md-fab" (^click)="click('fab')">BTN</button>
<button md-button class="md-fab md-primary" (^click)="click('fab primary')">PRMY</button>
<button md-button class="md-fab" disabled="disabled" (^click)="click('fab disabled')">DIS</button>
<button md-button class="md-fab md-accent" (^click)="click('fab accent')">ACC</button>
<button md-button class="md-fab md-warn" (^click)="click('fab warn')">WRN</button>
</section>
<section>
<span class="label">Anchor / hyperlink</span>
<a md-button href="http://google.com" target="_blank">HREF</a>
<a md-button href="http://google.com" disabled>DISABLED HREF</a>
<a md-button class="md-raised" target="_blank" href="http://google.com">RAISED HREF</a>
</section>
<p template="for #item of items">
Repeated button:
<button md-button tabindex="-1" (^click)="increment()">{{action}}</button>
{{clickCount}}
</p>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material button demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style> * { font-family: RobotoDraft, Roboto, 'Helvetica Neue', sans-serif; } </style>
</head>
<body>
<demo-app>Loading...</demo-app>
$SCRIPTS$
</body>
</html>

View File

@ -0,0 +1,46 @@
import {bootstrap, Component, View, MapWrapper, ListWrapper, For} from 'angular2/angular2';
import {MdButton, MdAnchor} from 'angular2_material/src/components/button/button'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdButton, MdAnchor, For]
})
class DemoApp {
previousClick: string;
action: string;
clickCount: number;
items: List<number>;
constructor() {
this.previousClick = 'Nothing';
this.action = "ACTIVATE";
this.clickCount = 0;
this.items = [1,2,3,4,5,6,7,8,9,0];
}
click(msg: string) {
this.previousClick = msg;
}
submit(msg: string, event) {
event.preventDefault();
this.previousClick = msg;
}
increment() {
this.clickCount++;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,9 @@
<div md-theme="default">
<h2>Checkbox demo</h2>
<md-checkbox (^click)="increment()">Normal checkbox</md-checkbox>
<md-checkbox class="md-primary" (^click)="increment()">Primary checkbox</md-checkbox>
<md-checkbox disabled (^click)="increment()">Disabled checkbox</md-checkbox>
<p>Toggle count: {{toggleCount}}</p>
</div>

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material checkbox demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,31 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdCheckbox} from 'angular2_material/src/components/checkbox/checkbox'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdCheckbox]
})
class DemoApp {
toggleCount: number;
constructor() {
this.toggleCount = 0;
}
increment() {
this.toggleCount++;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,71 @@
import {IMPLEMENTS, print} from 'angular2/src/facade/lang';
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {isPresent, isBlank, RegExpWrapper, StringWrapper, BaseException} from 'angular2/src/facade/lang';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {Injectable} from 'angular2/di';
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
import {reflector} from 'angular2/src/reflection/reflection';
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
export function commonDemoSetup(): void {
BrowserDomAdapter.makeCurrent();
reflector.reflectionCapabilities = new ReflectionCapabilities();
}
@Injectable()
@IMPLEMENTS(UrlResolver)
export class DemoUrlResolver {
static a;
isInPubServe:boolean;
constructor() {
if (isBlank(UrlResolver.a)) {
UrlResolver.a = DOM.createElement('a');
}
this.isInPubServe = _isInPubServe();
}
resolve(baseUrl: string, url: string): string {
if (isBlank(baseUrl)) {
DOM.resolveAndSetHref(UrlResolver.a, url, null);
return DOM.getHref(UrlResolver.a);
}
if (isBlank(url) || url == '') return baseUrl;
if (url[0] == '/') {
throw new BaseException(`Could not resolve the url ${url} from ${baseUrl}`);
}
var m = RegExpWrapper.firstMatch(_schemeRe, url);
if (isPresent(m[1])) {
return url;
}
if (StringWrapper.startsWith(url, './')) {
return `${baseUrl}/${url}`;
}
if (this.isInPubServe) {
return `/packages/${url}`;
} else {
return `/${url}`;
}
}
}
var _schemeRe = RegExpWrapper.create('^([^:/?#]+:)?');
// TODO: remove this hack when http://dartbug.com/23128 is fixed
function _isInPubServe():boolean {
try {
int.parse('123');
print('>> Running in Dart');
return true;
} catch(_) {
print('>> Running in JS');
return false;
}
}

View File

@ -0,0 +1,29 @@
<style>@import "angular2_material/src/components/grid_list/grid-list.css";</style>
<style>
md-grid-tile {
background-color: lightblue;
}
</style>
<div>
<h2>grid-list demo</h2>
<md-grid-list cols="5" gutter-size="2em">
<md-grid-tile cols="5">
Tile #1
</md-grid-tile>
<md-grid-tile rowspan="2" colspan="2">
Tile #2
</md-grid-tile>
<md-grid-tile [rowspan]="tile3RowSpan" [colspan]="tile3RowSpan">
Tile #3
</md-grid-tile>
</md-grid-list>
</div>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material grid-list demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,30 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdGridList, MdGridTile} from 'angular2_material/src/components/grid_list/grid_list'
import {MdTheme} from 'angular2_material/src/core/theme'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdGridList, MdGridTile]
})
class DemoApp {
tile3RowSpan: number;
tile3ColSpan: number;
constructor() {
this.tile3RowSpan = 3;
this.tile3ColSpan = 3;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,10 @@
<style>@import "angular2_material/src/components/input/input.css";</style>
<div md-theme="default">
<h2>input demo</h2>
<md-input-container>
<label>Name</label>
<input>
</md-input-container>
</div>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material input demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,31 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdCheckbox} from 'angular2_material/src/components/checkbox/checkbox'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdCheckbox]
})
class DemoApp {
toggleCount: number;
constructor() {
this.toggleCount = 0;
}
increment() {
this.toggleCount++;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,42 @@
<link>
<div style="width: 500px;">
<h2>Progress-linear demo</h2>
<p>
Determinate: primary
<md-progress-linear md-mode="determinate" [value]="progress" class="md-accent">
</md-progress-linear>
</p>
<p>
Determinate: accent
<md-progress-linear md-mode="determinate" [value]="progress" class="md-primary">
</md-progress-linear>
</p>
<p>
Buffer
<md-progress-linear md-mode="buffer"
[value]="progress" [buffer-value]="progress + (200 / progress)" class="md-warn">
</md-progress-linear>
</p>
<p>
Indeterminate
<md-progress-linear md-mode="indeterminate" class="md-primary">
</md-progress-linear>
</p>
<p>
Query
<md-progress-linear md-mode="query" class="md-accent">
</md-progress-linear>
</p>
<!--<md-progress-linear></md-progress-linear>-->
<p>Progress: {{progress}}</p>
<button type="button" (click)="step(10)">Increment</button>
<button type="button" (click)="step(-10)">Decrement</button>
</div>

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material progress-linear demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,31 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdProgressLinear} from 'angular2_material/src/components/progress-linear/progress_linear'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdProgressLinear]
})
class DemoApp {
progress: number;
constructor() {
this.progress = 40;
}
step(s: number) {
this.progress += s;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,35 @@
<div>
<h2>Radio buttons</h2>
<h3>Inside of a radiogroup</h3>
<md-radio-group #scifi (change)="onGroupChange()">
<md-radio-button value="star-wars">Star Wars</md-radio-button>
<md-radio-button value="star-trek" id="special-radio">Star Trek</md-radio-button>
<md-radio-button value="bsg" disabled>Battlestar Galactica</md-radio-button>
<md-radio-button [value]="thirdValue">Dr. Who</md-radio-button>
</md-radio-group>
<p>Your selection: {{scifi.value}}</p>
<p>radio group value change count: {{groupValueChangeCount}}</p>
<hr>
<h3>Standalone</h3>
<md-radio-button name="element" (^click)="onIndividualClick()">Earth</md-radio-button>
<md-radio-button name="element" (^click)="onIndividualClick()">Fire</md-radio-button>
<md-radio-button name="element" (^click)="onIndividualClick()">Wind</md-radio-button>
<md-radio-button name="element" (^click)="onIndividualClick()" disabled>Heart (disabled)</md-radio-button>
<p>individual radio value change count: {{individualValueChanges}}</p>
<hr>
<h3>Disabled radio group</h3>
<p>Chosen: {{pokemon}}</p>
<md-radio-group disabled [value]="pokemon">
<md-radio-button value="fire">Charmander</md-radio-button>
<md-radio-button value="leaf">Bulbasaur</md-radio-button>
<md-radio-button value="water">Squirtle</md-radio-button>
</md-radio-group>
<button type="button" (click)="chooseCharmander()">Choose Charmander</button>
</div>

View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material radio demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,49 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdRadioButton, MdRadioGroup} from 'angular2_material/src/components/radio/radio_button'
import {MdRadioDispatcher} from 'angular2_material/src/components/radio/radio_dispatcher'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app',
injectables: [MdRadioDispatcher]
})
@View({
templateUrl: './demo_app.html',
directives: [MdRadioGroup, MdRadioButton]
})
class DemoApp {
thirdValue;
groupValueChangeCount;
individualValueChanges;
pokemon;
someTabindex;
constructor() {
this.thirdValue = 'dr-who';
this.groupValueChangeCount = 0;
this.individualValueChanges = 0;
this.pokemon = '';
this.someTabindex = 888;
}
chooseCharmander() {
this.pokemon = 'fire';
}
onGroupChange() {
this.groupValueChangeCount++;
}
onIndividualClick() {
this.individualValueChanges++;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -0,0 +1,9 @@
<div md-theme="default">
<h2>Switch demo</h2>
<md-switch (^click)="increment()">Normal switch</md-switch>
<md-switch class="md-primary" (^click)="increment()">Primary switch</md-switch>
<md-switch disabled (^click)="increment()">Disabled switch</md-switch>
<p>Toggle count: {{toggleCount}}</p>
</div>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>ng-material switch demo</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:400,500,700,400italic">
<style>
* {
font-family: RobotoDraft, Roboto;
}
</style>
</head>
<body>
$SCRIPTS$
<demo-app>Loading...</demo-app>
</body>
</html>

View File

@ -0,0 +1,31 @@
import {bootstrap, Component, View} from 'angular2/angular2';
import {MdSwitch} from 'angular2_material/src/components/switcher/switch'
import {UrlResolver} from 'angular2/src/services/url_resolver';
import {commonDemoSetup, DemoUrlResolver} from '../demo_common';
import {bind} from 'angular2/di';
@Component({
selector: 'demo-app'
})
@View({
templateUrl: './demo_app.html',
directives: [MdSwitch]
})
class DemoApp {
toggleCount: number;
constructor() {
this.toggleCount = 0;
}
increment() {
this.toggleCount++;
}
}
export function main() {
commonDemoSetup();
bootstrap(DemoApp, [
bind(UrlResolver).toValue(new DemoUrlResolver())
]);
}

View File

@ -61,6 +61,7 @@
"fs-extra": "^0.16.4",
"glob": "^4.0.6",
"gulp": "^3.8.8",
"gulp-autoprefixer": "^2.1.0",
"gulp-changed": "^1.0.0",
"gulp-clang-format": "^1.0.3",
"gulp-concat": "^2.5.2",
@ -68,6 +69,7 @@
"gulp-jasmine": "^1.0.1",
"gulp-load-plugins": "^0.7.1",
"gulp-rename": "^1.2.0",
"gulp-sass": "^1.3.3",
"gulp-shell": "^0.2.10",
"gulp-sourcemaps": "1.3.*",
"gulp-template": "^3.0.0",

View File

@ -132,7 +132,7 @@ function getDocsTree() {
var mdTree = stew.rename(modulesFunnel(['**/*.dart.md']),
relativePath => relativePath.replace(/\.dart\.md$/, '.md'));
// Copy all assets, ignore .js. and .dart. (handled above).
var docs = modulesFunnel(['**/*.md', '**/*.png', '**/*.html', '**/*.css'],
var docs = modulesFunnel(['**/*.md', '**/*.png', '**/*.html', '**/*.css', '**/*.scss'],
['**/*.js.md', '**/*.dart.md']);
return mergeTrees([licenses, mdTree, docs]);
}