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
@@ -0,0 +1,62 @@
<div ng-controller="AppCtrl" layout="column" layout-fill>
<section layout="row" flex>
<md-sidenav class="md-sidenav-left md-whiteframe-z2" md-component-id="left" md-is-locked-open="$media('gt-md')">
<md-toolbar class="md-theme-indigo">
<h1 class="md-toolbar-tools">Sidenav Left</h1>
</md-toolbar>
<md-content class="md-padding" ng-controller="LeftCtrl">
<md-button ng-click="close()" class="md-primary" hide-gt-md>
Close Sidenav Left
</md-button>
<p hide-md show-gt-md>
This sidenav is locked open on your device. To go back to the default behavior,
narrow your display.
</p>
</md-content>
</md-sidenav>
<md-content flex class="md-padding">
<div layout="column" layout-fill layout-align="center center">
<p>
The left sidenav will 'lock open' on a medium (>=960px wide) device.
</p>
<div>
<md-button ng-click="toggleLeft()"
class="md-primary" hide-gt-md>
Toggle left
</md-button>
</div>
<div>
<md-button ng-click="toggleRight()"
class="md-primary">
Toggle right
</md-button>
</div>
</div>
</md-content>
<md-sidenav class="md-sidenav-right md-whiteframe-z2" md-component-id="right">
<md-toolbar class="md-theme-light">
<h1 class="md-toolbar-tools">Sidenav Right</h1>
</md-toolbar>
<md-content ng-controller="RightCtrl" class="md-padding">
<md-button ng-click="close()" class="md-primary">
Close Sidenav Right
</md-button>
</md-content>
</md-sidenav>
</section>
</div>
@@ -0,0 +1,36 @@
angular.module('sidenavDemo1', ['ngMaterial'])
.controller('AppCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.toggleLeft = function() {
$mdSidenav('left').toggle()
.then(function(){
$log.debug("toggle left is done");
});
};
$scope.toggleRight = function() {
$mdSidenav('right').toggle()
.then(function(){
$log.debug("toggle RIGHT is done");
});
};
})
.controller('LeftCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.close = function() {
$mdSidenav('left').close()
.then(function(){
$log.debug("close LEFT is done");
});
};
})
.controller('RightCtrl', function($scope, $timeout, $mdSidenav, $log) {
$scope.close = function() {
$mdSidenav('right').close()
.then(function(){
$log.debug("close RIGHT is done");
});
};
});
@@ -0,0 +1,3 @@
md-sidenav.md-THEME_NAME-theme {
background-color: '{{background-hue-3}}';
}
@@ -0,0 +1,293 @@
(function() {
'use strict';
/**
* @ngdoc module
* @name material.components.sidenav
*
* @description
* A Sidenav QP component.
*/
angular.module('material.components.sidenav', [
'material.core',
'material.components.backdrop'
])
.factory('$mdSidenav', SidenavService )
.directive('mdSidenav', SidenavDirective)
.controller('$mdSidenavController', SidenavController);
/**
* @private
* @ngdoc service
* @name $mdSidenav
* @module material.components.sidenav
*
* @description
* $mdSidenav makes it easy to interact with multiple sidenavs
* in an app.
*
* @usage
*
* ```javascript
* // Toggle the given sidenav
* $mdSidenav(componentId).toggle();
*
* // Open the given sidenav
* $mdSidenav(componentId).open();
*
* // Close the given sidenav
* $mdSidenav(componentId).close();
* ```
*/
function SidenavService($mdComponentRegistry, $q) {
return function(handle) {
var errorMsg = "SideNav '" + handle + "' is not available!";
// Lookup the controller instance for the specified sidNav instance
var instance = $mdComponentRegistry.get(handle);
if(!instance) {
$mdComponentRegistry.notFoundError(handle);
}
return {
isOpen: function() {
return instance && instance.isOpen();
},
toggle: function() {
return instance ? instance.toggle() : $q.reject(errorMsg);
},
open: function() {
return instance ? instance.open() : $q.reject(errorMsg);
},
close: function() {
return instance ? instance.close() : $q.reject(errorMsg);
}
};
};
}
/**
* @ngdoc directive
* @name mdSidenav
* @module material.components.sidenav
* @restrict E
*
* @description
*
* A Sidenav component that can be opened and closed programatically.
*
* By default, upon opening it will slide out on top of the main content area.
*
* @usage
* <hljs lang="html">
* <div layout="row" ng-controller="MyController">
* <md-sidenav md-component-id="left" class="md-sidenav-left">
* Left Nav!
* </md-sidenav>
*
* <md-content>
* Center Content
* <md-button ng-click="openLeftMenu()">
* Open Left Menu
* </md-button>
* </md-content>
*
* <md-sidenav md-component-id="right"
* md-is-locked-open="$media('min-width: 333px')"
* class="md-sidenav-right">
* Right Nav!
* </md-sidenav>
* </div>
* </hljs>
*
* <hljs lang="js">
* var app = angular.module('myApp', ['ngMaterial']);
* app.controller('MyController', function($scope, $mdSidenav) {
* $scope.openLeftMenu = function() {
* $mdSidenav('left').toggle();
* };
* });
* </hljs>
*
* @param {expression=} md-is-open A model bound to whether the sidenav is opened.
* @param {string=} md-component-id componentId to use with $mdSidenav service.
* @param {expression=} md-is-locked-open When this expression evalutes to true,
* the sidenav 'locks open': it falls into the content's flow instead
* of appearing over it. This overrides the `is-open` attribute.
*
* A $media() function is exposed to the is-locked-open attribute, which
* can be given a media query or one of the `sm`, `gt-sm`, `md`, `gt-md`, `lg` or `gt-lg` presets.
* Examples:
*
* - `<md-sidenav md-is-locked-open="shouldLockOpen"></md-sidenav>`
* - `<md-sidenav md-is-locked-open="$media('min-width: 1000px')"></md-sidenav>`
* - `<md-sidenav md-is-locked-open="$media('sm')"></md-sidenav>` (locks open on small screens)
*/
function SidenavDirective($timeout, $animate, $parse, $mdMedia, $mdConstant, $compile, $mdTheming, $q, $document) {
return {
restrict: 'E',
scope: {
isOpen: '=?mdIsOpen'
},
controller: '$mdSidenavController',
compile: function(element) {
element.addClass('md-closed');
element.attr('tabIndex', '-1');
return postLink;
}
};
/**
* Directive Post Link function...
*/
function postLink(scope, element, attr, sidenavCtrl) {
var triggeringElement = null;
var promise = $q.when(true);
var isLockedOpenParsed = $parse(attr.mdIsLockedOpen);
var isLocked = function() {
return isLockedOpenParsed(scope.$parent, {
$media: $mdMedia
});
};
var backdrop = $compile(
'<md-backdrop class="md-sidenav-backdrop md-opaque ng-enter">'
)(scope);
element.on('$destroy', sidenavCtrl.destroy);
$mdTheming.inherit(backdrop, element);
scope.$watch(isLocked, updateIsLocked);
scope.$watch('isOpen', updateIsOpen);
// Publish special accessor for the Controller instance
sidenavCtrl.$toggleOpen = toggleOpen;
/**
* Toggle the DOM classes to indicate `locked`
* @param isLocked
*/
function updateIsLocked(isLocked, oldValue) {
if (isLocked === oldValue) {
element.toggleClass('md-locked-open', !!isLocked);
} else {
$animate[isLocked ? 'addClass' : 'removeClass'](element, 'md-locked-open');
}
backdrop.toggleClass('md-locked-open', !!isLocked);
}
/**
* Toggle the SideNav view and attach/detach listeners
* @param isOpen
*/
function updateIsOpen(isOpen) {
var parent = element.parent();
parent[isOpen ? 'on' : 'off']('keydown', onKeyDown);
backdrop[isOpen ? 'on' : 'off']('click', close);
if ( isOpen ) {
// Capture upon opening..
triggeringElement = $document[0].activeElement;
}
return promise = $q.all([
$animate[isOpen ? 'enter' : 'leave'](backdrop, parent),
$animate[isOpen ? 'removeClass' : 'addClass'](element, 'md-closed').then(function() {
// If we opened, and haven't closed again before the animation finished
if (scope.isOpen) {
element.focus();
}
})
]);
}
/**
* Toggle the sideNav view and publish a promise to be resolved when
* the view animation finishes.
*
* @param isOpen
* @returns {*}
*/
function toggleOpen( isOpen ) {
if (scope.isOpen == isOpen ) {
return $q.when(true);
} else {
var deferred = $q.defer();
// Toggle value to force an async `updateIsOpen()` to run
scope.isOpen = isOpen;
$timeout(function() {
// When the current `updateIsOpen()` animation finishes
promise.then(function(result){
if ( !scope.isOpen ) {
// reset focus to originating element (if available) upon close
triggeringElement && triggeringElement.focus();
triggeringElement = null;
}
deferred.resolve(result);
});
},0,false);
return deferred.promise;
}
}
/**
* Auto-close sideNav when the `escape` key is pressed.
* @param evt
*/
function onKeyDown(ev) {
var isEscape = (ev.keyCode === $mdConstant.KEY_CODE.ESCAPE);
return isEscape ? close(ev) : $q.when(true);
}
/**
* With backdrop `clicks` or `escape` key-press, immediately
* apply the CSS close transition... Then notify the controller
* to close() and perform its own actions.
*/
function close(ev) {
ev.preventDefault();
ev.stopPropagation();
return sidenavCtrl.close();
}
}
}
/*
* @private
* @ngdoc controller
* @name SidenavController
* @module material.components.sidenav
*
*/
function SidenavController($scope, $element, $attrs, $mdComponentRegistry, $q) {
var self = this;
// Use Default internal method until overridden by directive postLink
self.$toggleOpen = function() { return $q.when($scope.isOpen); };
self.isOpen = function() { return !!$scope.isOpen; };
self.open = function() { return self.$toggleOpen( true ); };
self.close = function() { return self.$toggleOpen( false ); };
self.toggle = function() { return self.$toggleOpen( !$scope.isOpen ); };
self.destroy = $mdComponentRegistry.register(self, $attrs.mdComponentId);
}
})();
@@ -0,0 +1,93 @@
$sidenav-default-width: 304px !default;
md-sidenav {
position: absolute;
width: $sidenav-default-width;
min-width: $sidenav-default-width;
bottom: 0;
z-index: $z-index-sidenav;
background-color: white;
overflow: auto;
flex-direction: column;
&.md-closed {
display: none;
}
&.md-closed-add,
&.md-closed-remove {
display: flex;
/* this is required as of 1.3x to properly
apply all styling in a show/hide animation */
transition: 0s all;
}
&.md-closed-add.md-closed-add-active,
&.md-closed-remove.md-closed-remove-active {
transition: $swift-ease-out;
}
&.md-locked-open-add,
&.md-locked-open-remove {
position: static;
display: flex;
transform: translate3d(0, 0, 0);
}
&.md-locked-open {
width: $sidenav-default-width;
min-width: $sidenav-default-width;
}
&.md-locked-open,
&.md-locked-open.md-closed,
&.md-locked-open.md-closed.md-sidenav-left,
&.md-locked-open.md-closed.md-sidenav-right,
&.md-locked-open-remove.md-closed {
position: static;
display: flex;
transform: translate3d(0, 0, 0);
}
&.md-locked-open-remove-active {
transition: width $swift-ease-in-duration $swift-ease-in-timing-function,
min-width $swift-ease-in-duration $swift-ease-in-timing-function;
width: 0;
min-width: 0;
}
&.md-closed.md-locked-open-add {
width: 0;
min-width: 0;
transform: translate3d(0%, 0, 0);
}
&.md-closed.md-locked-open-add-active {
transition: width $swift-ease-in-duration $swift-ease-in-timing-function,
min-width $swift-ease-in-duration $swift-ease-in-timing-function;
width: $sidenav-default-width;
min-width: $sidenav-default-width;
transform: translate3d(0%, 0, 0);
}
@extend .md-sidenav-left;
}
.md-sidenav-backdrop.md-locked-open {
display: none;
}
.md-sidenav-left {
left: 0;
top: 0;
transform: translate3d(0%, 0, 0);
&.md-closed {
transform: translate3d(-100%, 0, 0);
}
}
.md-sidenav-right {
left: 100%;
top: 0;
transform: translate3d(-100%, 0, 0);
&.md-closed {
transform: translate3d(0%, 0, 0);
}
}
@@ -0,0 +1,239 @@
describe('mdSidenav', function() {
beforeEach(module('material.components.sidenav', 'ngAnimateMock', function($provide) {
$provide.value('$$rAF', function(cb) { cb(); });
}));
function setup(attrs) {
var el;
inject(function($compile, $rootScope) {
var parent = angular.element('<div>');
el = angular.element('<md-sidenav ' + (attrs||'') + '>');
parent.append(el);
$compile(parent)($rootScope);
$rootScope.$apply();
});
return el;
}
describe('directive', function() {
it('should bind isOpen attribute', inject(function($rootScope, $animate) {
var el = setup('md-is-open="show"');
$rootScope.$apply('show = true');
$animate.triggerCallbacks();
expect(el.hasClass('md-closed')).toBe(false);
expect(el.parent().find('md-backdrop').length).toBe(1);
$rootScope.$apply('show = false');
$animate.triggerCallbacks();
expect(el.hasClass('md-closed')).toBe(true);
expect(el.parent().find('md-backdrop').length).toBe(0);
}));
it('should close on escape', inject(function($rootScope, $animate, $mdConstant, $timeout) {
var el = setup('md-is-open="show"');
$rootScope.$apply('show = true');
$animate.triggerCallbacks();
el.parent().triggerHandler({
type: 'keydown',
keyCode: $mdConstant.KEY_CODE.ESCAPE
});
$timeout.flush();
expect($rootScope.show).toBe(false);
}));
it('should close on backdrop click', inject(function($rootScope, $animate, $timeout) {
var el = setup('md-is-open="show"');
$rootScope.$apply('show = true');
$animate.triggerCallbacks();
el.parent().find('md-backdrop').triggerHandler('click');
$timeout.flush();
expect($rootScope.show).toBe(false);
}));
it('should focus sidenav on open', inject(function($rootScope, $animate, $document) {
TestUtil.mockElementFocus(this);
var el = setup('md-is-open="show"');
$rootScope.$apply('show = true');
$animate.triggerCallbacks();
expect($document.activeElement).toBe(el[0]);
}));
it('should lock open when is-locked-open is true', inject(function($rootScope, $animate, $document) {
var el = setup('md-is-open="show" md-is-locked-open="lock"');
expect(el.hasClass('md-locked-open')).toBe(false);
$rootScope.$apply('lock = true');
expect(el.hasClass('md-locked-open')).toBe(true);
$rootScope.$apply('show = true');
$animate.triggerCallbacks();
expect(el.parent().find('md-backdrop').hasClass('md-locked-open')).toBe(true);
}));
it('should expose $mdMedia service as $media local in is-locked-open attribute', function() {
var mdMediaSpy = jasmine.createSpy('$mdMedia');
module(function($provide) {
$provide.value('$mdMedia', mdMediaSpy);
});
inject(function($rootScope, $animate, $document, $mdMedia) {
var el = setup('md-is-locked-open="$media(123)"');
expect($mdMedia).toHaveBeenCalledWith(123);
});
});
});
describe('controller', function() {
it('should create controller', function() {
var el = setup('');
var controller = el.controller('mdSidenav');
expect(controller).not.toBe(undefined);
});
it('should open and close and toggle', inject(function($timeout) {
var el = setup('');
var scope = el.isolateScope();
var controller = el.controller('mdSidenav');
// Should start closed
expect(el.hasClass('md-closed')).toBe(true);
controller.open();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(false);
controller.close();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(true);
controller.toggle();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(false);
}));
});
describe("controller Promise API", function() {
var $animate, $rootScope;
function flush() {
if ( !$rootScope.$$phase) {
$rootScope.$apply();
}
$animate.triggerCallbacks();
}
beforeEach( inject(function(_$animate_,_$rootScope_,_$timeout_) {
$animate = _$animate_;
$rootScope = _$rootScope_;
$timeout = _$timeout_;
}));
it('should open(), close(), and toggle() with promises', function () {
var el = setup('');
var scope = el.isolateScope();
var controller = el.controller('mdSidenav');
var openDone = 0, closeDone = 0, toggleDone = 0;
var onOpen = function() { openDone++; };
var onClose = function() { closeDone++; };
var onToggle = function() { toggleDone++; };
controller
.open()
.then(onOpen)
.then(controller.close)
.then(onClose);
flush();
expect(openDone).toBe(1);
flush();
expect(closeDone).toBe(1);
controller
.close()
.then(onClose);
flush();
expect(closeDone).toBe(2);
expect(scope.isOpen).toBe(false);
controller
.toggle()
.then(onToggle);
flush();
expect(toggleDone).toBe(1);
expect(scope.isOpen).toBe(true);
});
it('should open() to work multiple times before close()', function () {
var el = setup('');
var controller = el.controller('mdSidenav');
var openDone = 0, closeDone = 0;
var onOpen = function() { openDone++; };
var onClose = function() { closeDone++; };
controller
.open()
.then(onOpen)
.then(controller.open)
.then(onOpen);
flush();
expect(openDone).toBe(2);
expect(closeDone).toBe(0);
expect(el.hasClass('md-closed')).toBe(false);
controller
.close()
.then(onClose);
flush();
expect(openDone).toBe(2);
expect(closeDone).toBe(1);
expect(el.hasClass('md-closed')).toBe(true);
});
});
describe('$mdSidenav Service', function() {
it('should grab instance', inject(function($mdSidenav) {
var el = setup('md-component-id="left"');
var scope = el.isolateScope();
var instance = $mdSidenav('left');
expect(instance).toBeTruthy();
instance.open();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(false);
instance.close();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(true);
instance.toggle();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(false);
instance.toggle();
scope.$apply();
expect(el.hasClass('md-closed')).toBe(true);
}));
});
});