MODULE-TYPO

- Sass set-up
- md-list
- md-theming (install)
This commit is contained in:
iRouge
2015-01-16 07:52:29 -05:00
parent ec1b4b9b0c
commit f1d2b8cb75
312 changed files with 26839 additions and 1309 deletions

View File

@@ -0,0 +1,33 @@
<form ng-submit="submit()" ng-controller="AppCtrl" >
<p>Selected Value: <span class="radioValue">{{ data.group1 }}</span> </p>
<md-radio-group ng-model="data.group1">
<md-radio-button value="Apple" aria-label="Label 1">Apple</md-radio-button>
<md-radio-button value="Banana"> Banana </md-radio-button>
<md-radio-button value="Mango" aria-label="Label 3">Mango</md-radio-button>
</md-radio-group>
<hr />
<p>Selected Value: <span class="radioValue">{{ data.group2 }}</span></p>
<md-radio-group ng-model="data.group2">
<md-radio-button ng-repeat="d in radioData"
ng-value="d.value"
ng-disabled=" d.isDisabled "
aria-label="{{ d.label }}">
{{ d.label }}
</md-radio-button>
</md-radio-group>
<p>
<md-button ng-click="addItem()">Add</md-button>
<md-button ng-click="removeItem()">Remove</md-button>
</p>
</form>

View File

@@ -0,0 +1,31 @@
angular.module('radioDemo1', ['ngMaterial'])
.controller('AppCtrl', function($scope) {
$scope.data = {
group1 : 'Banana',
group2 : '2'
};
$scope.radioData = [
{ label: '1', value: 1 },
{ label: '2', value: 2 },
{ label: '3', value: '3', isDisabled: true },
{ label: '4', value: '4' }
];
$scope.submit = function() {
alert('submit');
};
$scope.addItem = function() {
var r = Math.ceil(Math.random() * 1000);
$scope.radioData.push({ label: r, value: r });
};
$scope.removeItem = function() {
$scope.radioData.pop();
};
});

View File

@@ -0,0 +1,29 @@
body {
padding: 20px;
}
hr {
margin-left:-20px; opacity:0.3;
}
md-radio-group {
width:150px;
}
p:last-child {
padding-bottom: 50px;
}
[ng-controller] {
padding-left: 20px;
}
.radioValue {
margin-left: 5px;
color: #0f9d58;
font-weight: bold;
padding:5px;
}

View File

@@ -0,0 +1,68 @@
md-radio-button.md-THEME_NAME-theme {
.md-off {
border-color: '{{foreground-2}}';
}
.md-on {
background-color: '{{accent-color-0.87}}';
}
&.md-checked .md-off {
border-color: '{{accent-color-0.87}}';
}
&.md-checked .md-ink-ripple {
color: '{{accent-color-0.87}}';
}
.md-container .md-ripple {
color: '{{accent-600}}';
}
&:not([disabled]) {
&.md-warn {
.md-on {
background-color: '{{warn-color-0.87}}';
}
&.md-checked .md-off {
border-color: '{{warn-color-0.87}}';
}
&.md-checked .md-ink-ripple {
color: '{{warn-color-0.87}}';
}
.md-container .md-ripple {
color: '{{warn-600}}';
}
}
&.md-primary {
.md-on {
background-color: '{{primary-color-0.87}}';
}
&.md-checked .md-off {
border-color: '{{primary-color-0.87}}';
}
&.md-checked .md-ink-ripple {
color: '{{primary-color-0.87}}';
}
.md-container .md-ripple {
color: '{{primary-600}}';
}
}
}
&[disabled] {
.md-container .md-off {
border-color: '{{foreground-3}}';
}
.md-container .md-on {
border-color: '{{foreground-3}}';
}
}
}
md-radio-group.md-THEME_NAME-theme:focus:not(:empty) {
border-color: '{{foreground-1}}';
}

View File

@@ -0,0 +1,80 @@
$radio-width: 16px !default;
$radio-height: $radio-width !default;
md-radio-button,
.md-switch-thumb { // Used in switch
display: block;
margin: 15px;
white-space: nowrap;
cursor: pointer;
input {
display: none;
}
.md-container {
position: relative;
top: 4px;
display: inline-block;
width: $radio-width;
height: $radio-width;
cursor: pointer;
.md-ripple-container {
position: absolute;
display: block;
width: $radio-width * 3;
height: $radio-width * 3;
left: -$radio-width;
top: -$radio-width;
}
}
.md-off {
position: absolute;
top: 0px;
left: 0px;
width: $radio-width;
height: $radio-width;
border: solid 2px;
border-radius: 50%;
transition: border-color ease 0.28s;
}
.md-on {
position: absolute;
top: 0;
left: 0;
width: $radio-width;
height: $radio-width;
border-radius: 50%;
transition: transform ease 0.28s;
transform: scale(0);
}
&.md-checked .md-on {
transform: scale(0.55);
}
.md-label {
position: relative;
display: inline-block;
margin-left: 10px;
margin-right: 10px;
vertical-align: middle;
white-space: normal;
pointer-events: none;
width: auto;
}
.circle {
border-radius: 50%;
}
}
md-radio-group {
border: 1px dotted transparent;
display: block;
outline: none;
}

View File

@@ -0,0 +1,288 @@
(function() {
'use strict';
/**
* @ngdoc module
* @name material.components.radioButton
* @description radioButton module!
*/
angular.module('material.components.radioButton', [
'material.core'
])
.directive('mdRadioGroup', mdRadioGroupDirective)
.directive('mdRadioButton', mdRadioButtonDirective);
/**
* @ngdoc directive
* @module material.components.radioButton
* @name mdRadioGroup
*
* @restrict E
*
* @description
* The `<md-radio-group>` directive identifies a grouping
* container for the 1..n grouped radio buttons; specified using nested
* `<md-radio-button>` tags.
*
* Note: `<md-radio-group>` and `<md-radio-button>` handle tabindex differently
* than the native `<input type='radio'>` controls. Whereas the native controls
* force the user to tab through all the radio buttons, `<md-radio-group>`
* is focusable, and by default the `<md-radio-button>`s are not.
*
* @param {string} ng-model Assignable angular expression to data-bind to.
* @param {boolean=} md-no-ink Use of attribute indicates flag to disable ink ripple effects.
*
* @usage
* <hljs lang="html">
* <md-radio-group ng-model="selected">
*
* <md-radio-button
* ng-repeat="d in colorOptions"
* ng-value="d.value" aria-label="{{ d.label }}">
*
* {{ d.label }}
*
* </md-radio-button>
*
* </md-radio-group>
* </hljs>
*
*/
function mdRadioGroupDirective($mdUtil, $mdConstant, $mdTheming) {
RadioGroupController.prototype = createRadioGroupControllerProto();
return {
restrict: 'E',
controller: ['$element', RadioGroupController],
require: ['mdRadioGroup', '?ngModel'],
link: linkRadioGroup
};
function linkRadioGroup(scope, element, attr, ctrls) {
$mdTheming(element);
var rgCtrl = ctrls[0];
var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel();
function keydownListener(ev) {
switch(ev.keyCode) {
case $mdConstant.KEY_CODE.LEFT_ARROW:
case $mdConstant.KEY_CODE.UP_ARROW:
ev.preventDefault();
rgCtrl.selectPrevious();
break;
case $mdConstant.KEY_CODE.RIGHT_ARROW:
case $mdConstant.KEY_CODE.DOWN_ARROW:
ev.preventDefault();
rgCtrl.selectNext();
break;
case $mdConstant.KEY_CODE.ENTER:
var form = angular.element($mdUtil.getClosest(element[0], 'form'));
if (form.length > 0) {
form.triggerHandler('submit');
}
break;
}
}
rgCtrl.init(ngModelCtrl);
element.attr({
'role': 'radiogroup',
'tabIndex': element.attr('tabindex') || '0'
})
.on('keydown', keydownListener);
}
function RadioGroupController($element) {
this._radioButtonRenderFns = [];
this.$element = $element;
}
function createRadioGroupControllerProto() {
return {
init: function(ngModelCtrl) {
this._ngModelCtrl = ngModelCtrl;
this._ngModelCtrl.$render = angular.bind(this, this.render);
},
add: function(rbRender) {
this._radioButtonRenderFns.push(rbRender);
},
remove: function(rbRender) {
var index = this._radioButtonRenderFns.indexOf(rbRender);
if (index !== -1) {
this._radioButtonRenderFns.splice(index, 1);
}
},
render: function() {
this._radioButtonRenderFns.forEach(function(rbRender) {
rbRender();
});
},
setViewValue: function(value, eventType) {
this._ngModelCtrl.$setViewValue(value, eventType);
// update the other radio buttons as well
this.render();
},
getViewValue: function() {
return this._ngModelCtrl.$viewValue;
},
selectNext: function() {
return changeSelectedButton(this.$element, 1);
},
selectPrevious : function() {
return changeSelectedButton(this.$element, -1);
},
setActiveDescendant: function (radioId) {
this.$element.attr('aria-activedescendant', radioId);
}
};
}
/**
* Change the radio group's selected button by a given increment.
* If no button is selected, select the first button.
*/
function changeSelectedButton(parent, increment) {
// Coerce all child radio buttons into an array, then wrap then in an iterator
var buttons = $mdUtil.iterator(
Array.prototype.slice.call(parent[0].querySelectorAll('md-radio-button')),
true
);
if (buttons.count()) {
var validate = function (button) {
// If disabled, then NOT valid
return !angular.element(button).attr("disabled");
};
var selected = parent[0].querySelector('md-radio-button.md-checked');
var target = buttons[increment < 0 ? 'previous' : 'next'](selected, validate) || buttons.first();
// Activate radioButton's click listener (triggerHandler won't create a real click event)
angular.element(target).triggerHandler('click');
}
}
}
/**
* @ngdoc directive
* @module material.components.radioButton
* @name mdRadioButton
*
* @restrict E
*
* @description
* The `<md-radio-button>`directive is the child directive required to be used within `<md-radio-group>` elements.
*
* While similar to the `<input type="radio" ng-model="" value="">` directive,
* the `<md-radio-button>` directive provides ink effects, ARIA support, and
* supports use within named radio groups.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {string} ngValue Angular expression which sets the value to which the expression should
* be set when selected.*
* @param {string} value The value to which the expression should be set when selected.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} ariaLabel Adds label to radio button for accessibility.
* Defaults to radio button's text. If no default text is found, a warning will be logged.
*
* @usage
* <hljs lang="html">
*
* <md-radio-button value="1" aria-label="Label 1">
* Label 1
* </md-radio-button>
*
* <md-radio-button ng-model="color" ng-value="specialValue" aria-label="Green">
* Green
* </md-radio-button>
*
* </hljs>
*
*/
function mdRadioButtonDirective($mdAria, $mdUtil, $mdTheming) {
var CHECKED_CSS = 'md-checked';
return {
restrict: 'E',
require: '^mdRadioGroup',
transclude: true,
template: '<div class="md-container" md-ink-ripple md-ink-ripple-checkbox>' +
'<div class="md-off"></div>' +
'<div class="md-on"></div>' +
'</div>' +
'<div ng-transclude class="md-label"></div>',
link: link
};
function link(scope, element, attr, rgCtrl) {
var lastChecked;
$mdTheming(element);
configureAria(element, scope);
rgCtrl.add(render);
attr.$observe('value', render);
element
.on('click', listener)
.on('$destroy', function() {
rgCtrl.remove(render);
});
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
scope.$apply(function() {
rgCtrl.setViewValue(attr.value, ev && ev.type);
});
}
function render() {
var checked = (rgCtrl.getViewValue() == attr.value);
if (checked === lastChecked) {
return;
}
lastChecked = checked;
element.attr('aria-checked', checked);
if (checked) {
element.addClass(CHECKED_CSS);
rgCtrl.setActiveDescendant(element.attr('id'));
} else {
element.removeClass(CHECKED_CSS);
}
}
/**
* Inject ARIA-specific attributes appropriate for each radio button
*/
function configureAria( element, scope ){
scope.ariaId = buildAriaID();
element.attr({
'id' : scope.ariaId,
'role' : 'radio',
'aria-checked' : 'false'
});
$mdAria.expectWithText(element, 'aria-label');
/**
* Build a unique ID for each radio button that will be used with aria-activedescendant.
* Preserve existing ID if already specified.
* @returns {*|string}
*/
function buildAriaID() {
return attr.id || ( 'radio' + "_" + $mdUtil.nextUid() );
}
}
}
}
})();

View File

@@ -0,0 +1,258 @@
describe('radioButton', function() {
var CHECKED_CSS = 'md-checked';
beforeEach(TestUtil.mockRaf);
beforeEach(module('material.components.radioButton'));
it('should set checked css class', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
$rootScope.$apply(function(){
$rootScope.color = 'green';
});
var rbElements = element.find('md-radio-button');
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toEqual(false);
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toEqual(true);
}));
it('should support mixed values', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="value">' +
'<md-radio-button value="1"></md-radio-button>' +
'<md-radio-button value="2"></md-radio-button>' +
'</md-radio-group>')($rootScope);
$rootScope.$apply(function(){
$rootScope.value = 1;
});
var rbElements = element.find('md-radio-button');
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toEqual(true);
}));
it('should set roles', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
var rbGroupElement = element;
expect(rbGroupElement.eq(0).attr('role')).toEqual('radiogroup');
expect(rbGroupElement.find('md-radio-button').eq(0).attr('role')).toEqual('radio');
}));
it('should set aria states', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
$rootScope.$apply(function(){
$rootScope.color = 'green';
});
var rbElements = element.find('md-radio-button');
expect(rbElements.eq(0).attr('aria-checked')).toEqual('false');
expect(rbElements.eq(1).attr('aria-checked')).toEqual('true');
expect(element.attr('aria-activedescendant')).toEqual(rbElements.eq(1).attr('id'));
expect(element.attr('aria-activedescendant')).not.toEqual(rbElements.eq(0).attr('id'));
}));
it('should warn developers they need a label', inject(function($compile, $rootScope, $log){
spyOn($log, "warn");
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
expect($log.warn).toHaveBeenCalled();
}));
it('should create an aria label from provided text', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue">Blue</md-radio-button>' +
'<md-radio-button value="green">Green</md-radio-button>' +
'</md-radio-group>')($rootScope);
var rbElements = element.find('md-radio-button');
expect(rbElements.eq(0).attr('aria-label')).toEqual('Blue');
}));
it('should preserve tabindex', inject(function($compile, $rootScope, $mdConstant) {
var element = $compile('<md-radio-group ng-model="color" tabindex="3">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
var rbGroupElement = element.eq(0);
expect(rbGroupElement.attr('tabindex')).toEqual('3');
}));
it('should be operable via arrow keys', inject(function($compile, $rootScope, $mdConstant) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="blue"></md-radio-button>' +
'<md-radio-button value="green"></md-radio-button>' +
'</md-radio-group>')($rootScope);
$rootScope.$apply(function(){
$rootScope.color = 'blue';
});
var rbGroupElement = element.eq(0);
rbGroupElement.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW
});
expect($rootScope.color).toEqual('green');
}));
describe('ng core radio button tests', function() {
it('should noop with no model', inject(function($compile, $rootScope) {
var el;
expect(function() {
el = $compile('<md-radio-group>' +
'<md-radio-button value="white">' +
'</md-radio-group>')($rootScope);
}).not.toThrow();
var rbElements = el.find('md-radio-button');
// Fire off the render function with no ngModel, make sure nothing
// goes unexpectedly.
expect(function() {
rbElements.eq(0).triggerHandler('click');
}).not.toThrow();
}));
it('should update the model', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="white"></md-radio-button>' +
'<md-radio-button value="red"></md-radio-button>' +
'<md-radio-button value="blue"></md-radio-button>' +
'</md-radio-group>')($rootScope);
var rbElements = element.find('md-radio-button');
$rootScope.$apply("color = 'white'");
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toBe(true);
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toBe(false);
expect(rbElements.eq(2).hasClass(CHECKED_CSS)).toBe(false);
$rootScope.$apply("color = 'red'");
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toBe(false);
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toBe(true);
expect(rbElements.eq(2).hasClass(CHECKED_CSS)).toBe(false);
rbElements.eq(2).triggerHandler('click');
expect($rootScope.color).toBe('blue');
}));
it('should trigger a submit', inject(function($compile, $rootScope, $mdConstant) {
$rootScope.testValue = false;
$rootScope.submitFn = function(){
$rootScope.testValue = true;
};
var element = $compile('<div><form ng-submit="submitFn()">' +
'<md-radio-group ng-model="color">' +
'<md-radio-button value="white"></md-radio-button>' +
'</md-radio-group>' +
'</form></div>')($rootScope);
var formElement = element.find('form'),
rbGroupElement = element.find('md-radio-group');
rbGroupElement.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.ENTER
});
expect($rootScope.testValue).toBe(true);
}));
it('should be disabled', inject(function($compile, $rootScope) {
var element = $compile('<md-radio-group ng-model="color">' +
'<md-radio-button value="white" ng-disabled="isDisabled"></md-radio-button>' +
'</md-radio-group>')($rootScope);
var radio = element.find('md-radio-button');
$rootScope.$apply('isDisabled = true');
$rootScope.$apply('color = null');
radio.triggerHandler('click');
expect($rootScope.color).toBe(null);
$rootScope.$apply('isDisabled = false');
radio.triggerHandler('click');
expect($rootScope.color).toBe('white');
}));
it('should skip disabled on arrow key', inject(function($compile, $rootScope, $mdConstant) {
var element = $compile(
'<md-radio-group ng-model="color">' +
' <md-radio-button value="red" ></md-radio-button>' +
' <md-radio-button value="white" ng-disabled="isDisabled"></md-radio-button>' +
' <md-radio-button value="blue" ></md-radio-button>' +
'</md-radio-group>'
)($rootScope);
var rbGroupElement = element.eq(0);
$rootScope.$apply('isDisabled = true');
$rootScope.$apply('color = "red"');
expect($rootScope.color).toBe("red");
rightArrow(); expect($rootScope.color).toEqual('blue');
rightArrow(); expect($rootScope.color).toEqual('red');
rightArrow(); expect($rootScope.color).toEqual('blue');
$rootScope.$apply('isDisabled = false');
rightArrow();
rightArrow(); expect($rootScope.color).toEqual('white');
rightArrow(); expect($rootScope.color).toEqual('blue');
function rightArrow() {
rbGroupElement.triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.RIGHT_ARROW
});
}
}));
it('should allow {{expr}} as value', inject(function($compile, $rootScope) {
$rootScope.some = 11;
var element = $compile('<md-radio-group ng-model="value">' +
'<md-radio-button value="{{some}}"></md-radio-button>' +
'<md-radio-button value="{{other}}"></<md-radio-button>' +
'</md-radio-group>')($rootScope);
var rbElements = element.find('md-radio-button');
$rootScope.$apply(function() {
$rootScope.value = 'blue';
$rootScope.some = 'blue';
$rootScope.other = 'red';
});
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toBe(true);
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toBe(false);
rbElements.eq(1).triggerHandler('click');
expect($rootScope.value).toBe('red');
$rootScope.$apply("other = 'non-red'");
expect(rbElements.eq(0).hasClass(CHECKED_CSS)).toBe(false);
expect(rbElements.eq(1).hasClass(CHECKED_CSS)).toBe(false);
}));
});
});