+
I flex to one-third of the space on mobile, and two-thirds on other devices.
-
+
I flex to two-thirds of the space on mobile, and one-third on other devices.
diff --git a/src/core/services/layout/layout.js b/src/core/services/layout/layout.js
index 436ebb8b693..9b8f7d866bd 100644
--- a/src/core/services/layout/layout.js
+++ b/src/core/services/layout/layout.js
@@ -1,319 +1,414 @@
-(function () {
+(function() {
'use strict';
- var $mdUtil, $$mdLayout, $parse, $interpolate;
+ var $mdUtil, $$mdLayout, $interpolate;
+ var SUFFICES = /(-sm|-gt-sm|-md|-gt-md|-lg|-gt-lg)/g;
+ var WHITESPACE = /\s+/g;
+
+ var FLEX_OPTIONS = ['grow', 'initial', 'auto', 'none'];
+ var LAYOUT_OPTIONS = ['row', 'column'];
+ var ALIGNMENT_OPTIONS = [
+ "start start", "start center", "start end",
+ "center", "center center", "center start", "center end",
+ "end", "end center", "end start", "end end",
+ "space-around", "space-around center", "space-around start", "space-around end",
+ "space-between", "space-between center", "space-between start", "space-between end"
+ ];
+
+
+ var config = {
/**
+ * Enable directive attribute-to-class conversions
+ */
+ enabled: true,
+
+ /**
+ * List of mediaQuery breakpoints and associated suffixes
*
- * The original ngMaterial Layout solution used attribute selectors and CSS.
- *
- * ```html
- *
My Content
- * ```
- *
- * ```css
- * [layout] {
+ * [
+ * { suffix: "sm", mediaQuery: "screen and (max-width: 599px)" },
+ * { suffix: "md", mediaQuery: "screen and (min-width: 600px) and (max-width: 959px)" }
+ * ]
+ */
+ breakpoints: []
+ };
+
+ /**
+ * The original ngMaterial Layout solution used attribute selectors and CSS.
+ *
+ * ```html
+ *
My Content
+ * ```
+ *
+ * ```css
+ * [layout] {
* box-sizing: border-box;
* display:flex;
* }
- * [layout=column] {
+ * [layout=column] {
* flex-direction : column
* }
- * ```
- *
- * Use of attribute selectors creates significant performance impacts in some
- * browsers... mainly IE.
- *
- * This module registers directives that allow the same layout attributes to be
- * interpreted and converted to class selectors. The directive will add equivalent classes to each element that
- * contains a Layout directive.
- *
- * ```html
- *
My Content
- *```
- *
- * ```css
- * .layout {
+ * ```
+ *
+ * Use of attribute selectors creates significant performance impacts in some
+ * browsers... mainly IE.
+ *
+ * This module registers directives that allow the same layout attributes to be
+ * interpreted and converted to class selectors. The directive will add equivalent classes to each element that
+ * contains a Layout directive.
+ *
+ * ```html
+ *
My Content
+ *```
+ *
+ * ```css
+ * .layout {
* box-sizing: border-box;
* display:flex;
* }
- * .layout-column {
+ * .layout-column {
* flex-direction : column
* }
- * ```
- */
- angular.module('material.core.layout', [ 'ng' ])
-
- /**
- * Model of flags used by the Layout directives
- * Allows changes while running tests or runtime app changes
- */
- .factory("$$mdLayout", function() {
- return {
- removeAttributes : true
-
- };
- })
-
- // Attribute directives with optional value(s)
-
- .directive('layout' , attributeWithObserve('layout' ) )
- .directive('layoutSm' , attributeWithObserve('layout-sm' ) )
- .directive('layoutGtSm' , attributeWithObserve('layout-gt-sm') )
- .directive('layoutMd' , attributeWithObserve('layout-md' ) )
- .directive('layoutGtMd' , attributeWithObserve('layout-gt-md') )
- .directive('layoutLg' , attributeWithObserve('layout-lg' ) )
- .directive('layoutGtLg' , attributeWithObserve('layout-gt-lg') )
-
- .directive('flex' , attributeWithObserve('flex' ) )
- .directive('flexSm' , attributeWithObserve('flex-sm' ) )
- .directive('flexGtSm' , attributeWithObserve('flex-gt-sm' ) )
- .directive('flexMd' , attributeWithObserve('flex-md' ) )
- .directive('flexGtMd' , attributeWithObserve('flex-gt-md' ) )
- .directive('flexLg' , attributeWithObserve('flex-lg' ) )
- .directive('flexGtLg' , attributeWithObserve('flex-gt-lg' ) )
-
- // Attribute directives with optional value(s) but directiveName is NOT added as a class
-
- .directive('layoutAlign' , attributeWithObserve('layout-align') )
- .directive('layoutAlignSm' , attributeWithObserve('layout-align-sm') )
- .directive('layoutAlignGtSm' , attributeWithObserve('layout-align-gt-sm') )
- .directive('layoutAlignMd' , attributeWithObserve('layout-align-md') )
- .directive('layoutAlignGtMd' , attributeWithObserve('layout-align-gt-md') )
- .directive('layoutAlignLg' , attributeWithObserve('layout-align-lg') )
- .directive('layoutAlignGtLg' , attributeWithObserve('layout-align-gt-lg') )
-
- .directive('flexOrder' , attributeWithObserve('flex-order') )
- .directive('flexOrderSm' , attributeWithObserve('flex-order-sm') )
- .directive('flexOrderGtSm' , attributeWithObserve('flex-order-gt-sm') )
- .directive('flexOrderMd' , attributeWithObserve('flex-order-md') )
- .directive('flexOrderGtMd' , attributeWithObserve('flex-order-gt-md') )
- .directive('flexOrderLg' , attributeWithObserve('flex-order-lg') )
- .directive('flexOrderGtLg' , attributeWithObserve('flex-order-gt-lg') )
-
- .directive('offset' , attributeWithObserve('offset') )
- .directive('offsetSm' , attributeWithObserve('offset-sm') )
- .directive('offsetGtSm' , attributeWithObserve('offset-gt-sm') )
- .directive('offsetMd' , attributeWithObserve('offset-md') )
- .directive('offsetGtMd' , attributeWithObserve('offset-gt-md') )
- .directive('offsetLg' , attributeWithObserve('offset-lg') )
- .directive('offsetGtLg' , attributeWithObserve('offset-gt-lg') )
-
- // Attribute directives with no value(s)
-
- .directive('layoutMargin' , attributeWithoutValue('layout-margin') )
- .directive('layoutPadding' , attributeWithoutValue('layout-padding') )
- .directive('layoutWrap' , attributeWithoutValue('layout-wrap') )
- .directive('layoutFill' , attributeWithoutValue('layout-fill') )
-
- .directive('hide' , attributeWithoutValue('hide') )
- .directive('hideSm' , attributeWithoutValue('hide-sm') )
- .directive('hideGtSm' , attributeWithoutValue('hide-gt-sm') )
- .directive('hideMd' , attributeWithoutValue('hide-md') )
- .directive('hideGtMd' , attributeWithoutValue('hide-gt-md') )
- .directive('hideLg' , attributeWithoutValue('hide-lg') )
- .directive('hideGtLg' , attributeWithoutValue('hide-gt-lg') )
- .directive('show' , attributeWithoutValue('show') )
- .directive('showSm' , attributeWithoutValue('show-sm') )
- .directive('showGtSm' , attributeWithoutValue('show-gt-sm') )
- .directive('showMd' , attributeWithoutValue('show-md') )
- .directive('showGtMd' , attributeWithoutValue('show-gt-md') )
- .directive('showLg' , attributeWithoutValue('show-lg') )
- .directive('showGtLg' , attributeWithoutValue('show-gt-lg') )
-
- // !! Deprecated attributes: use the `-lt` (aka less-than) notations
-
- .directive('layoutLtMd' , warnAttrNotSupported('layout-lt-md',true) )
- .directive('layoutLtLg' , warnAttrNotSupported('layout-lt-lg',true) )
- .directive('flexLtMd' , warnAttrNotSupported('flex-lt-md' ,true) )
- .directive('flexLtLg' , warnAttrNotSupported('flex-lt-lg' ,true) )
-
- .directive('layoutAlignLtMd' , warnAttrNotSupported('layout-align-lt-md') )
- .directive('layoutAlignLtLg' , warnAttrNotSupported('layout-align-lt-lg') )
- .directive('flexOrderLtMd' , warnAttrNotSupported('flex-order-lt-md') )
- .directive('flexOrderLtLg' , warnAttrNotSupported('flex-order-lt-lg') )
- .directive('offsetLtMd' , warnAttrNotSupported('offset-lt-md') )
- .directive('offsetLtLg' , warnAttrNotSupported('offset-lt-lg') )
-
- .directive('hideLtMd' , warnAttrNotSupported ('hide-lt-md') )
- .directive('hideLtLg' , warnAttrNotSupported ('hide-lt-lg') )
- .directive('showLtMd' , warnAttrNotSupported ('show-lt-md') )
- .directive('showLtLg' , warnAttrNotSupported ('show-lt-lg') );
+ * ```
+ */
+ angular.module('material.core.layout', ['ng'])
+
+ /**
+ * Model of flags used by the Layout directives
+ * Allows changes while running tests or runtime app changes
+ */
+ .factory("$$mdLayout", function() {
+ return {
+ removeAttributes: true
+ };
+ })
+
+ // Attribute directives with optional value(s)
+
+ .directive('layout', attributeWithObserve('layout'))
+ .directive('layoutSm', attributeWithObserve('layout-sm'))
+ .directive('layoutGtSm', attributeWithObserve('layout-gt-sm'))
+ .directive('layoutMd', attributeWithObserve('layout-md'))
+ .directive('layoutGtMd', attributeWithObserve('layout-gt-md'))
+ .directive('layoutLg', attributeWithObserve('layout-lg'))
+ .directive('layoutGtLg', attributeWithObserve('layout-gt-lg'))
+
+ .directive('flex', attributeWithObserve('flex'))
+ .directive('flexSm', attributeWithObserve('flex-sm'))
+ .directive('flexGtSm', attributeWithObserve('flex-gt-sm'))
+ .directive('flexMd', attributeWithObserve('flex-md'))
+ .directive('flexGtMd', attributeWithObserve('flex-gt-md'))
+ .directive('flexLg', attributeWithObserve('flex-lg'))
+ .directive('flexGtLg', attributeWithObserve('flex-gt-lg'))
+
+ .directive('flexOrder', attributeWithObserve('flex-order'))
+ .directive('flexOrderSm', attributeWithObserve('flex-order-sm'))
+ .directive('flexOrderGtSm', attributeWithObserve('flex-order-gt-sm'))
+ .directive('flexOrderMd', attributeWithObserve('flex-order-md'))
+ .directive('flexOrderGtMd', attributeWithObserve('flex-order-gt-md'))
+ .directive('flexOrderLg', attributeWithObserve('flex-order-lg'))
+ .directive('flexOrderGtLg', attributeWithObserve('flex-order-gt-lg'))
+
+ .directive('offset', attributeWithObserve('layout-offset'))
+ .directive('offsetSm', attributeWithObserve('layout-offset-sm'))
+ .directive('offsetGtSm', attributeWithObserve('layout-offset-gt-sm'))
+ .directive('offsetMd', attributeWithObserve('layout-offset-md'))
+ .directive('offsetGtMd', attributeWithObserve('layout-offset-gt-md'))
+ .directive('offsetLg', attributeWithObserve('layout-offset-lg'))
+ .directive('offsetGtLg', attributeWithObserve('layout-offset-gt-lg'))
+ .directive('layoutOffset', attributeWithObserve('layout-offset'))
+ .directive('layoutOffsetSm', attributeWithObserve('layout-offset-sm'))
+ .directive('layoutOffsetGtSm', attributeWithObserve('layout-offset-gt-sm'))
+ .directive('layoutOffsetMd', attributeWithObserve('layout-offset-md'))
+ .directive('layoutOffsetGtMd', attributeWithObserve('layout-offset-gt-md'))
+ .directive('layoutOffsetLg', attributeWithObserve('layout-offset-lg'))
+ .directive('layoutOffsetGtLg', attributeWithObserve('layout-offset-gt-lg'))
+
+ .directive('layoutAlign', attributeWithObserve('layout-align'))
+ .directive('layoutAlignSm', attributeWithObserve('layout-align-sm'))
+ .directive('layoutAlignGtSm', attributeWithObserve('layout-align-gt-sm'))
+ .directive('layoutAlignMd', attributeWithObserve('layout-align-md'))
+ .directive('layoutAlignGtMd', attributeWithObserve('layout-align-gt-md'))
+ .directive('layoutAlignLg', attributeWithObserve('layout-align-lg'))
+ .directive('layoutAlignGtLg', attributeWithObserve('layout-align-gt-lg'))
+
+ // Attribute directives with no value(s)
+
+ .directive('hide', attributeWithoutValue('hide'))
+ .directive('hideSm', attributeWithoutValue('hide-sm'))
+ .directive('hideGtSm', attributeWithoutValue('hide-gt-sm'))
+ .directive('hideMd', attributeWithoutValue('hide-md'))
+ .directive('hideGtMd', attributeWithoutValue('hide-gt-md'))
+ .directive('hideLg', attributeWithoutValue('hide-lg'))
+ .directive('hideGtLg', attributeWithoutValue('hide-gt-lg'))
+ .directive('show', attributeWithoutValue('show'))
+ .directive('showSm', attributeWithoutValue('show-sm'))
+ .directive('showGtSm', attributeWithoutValue('show-gt-sm'))
+ .directive('showMd', attributeWithoutValue('show-md'))
+ .directive('showGtMd', attributeWithoutValue('show-gt-md'))
+ .directive('showLg', attributeWithoutValue('show-lg'))
+ .directive('showGtLg', attributeWithoutValue('show-gt-lg'))
+
+ // Attribute directives with no value(s) and NO breakpoints
+
+ .directive('layoutMargin', attributeWithoutValue('layout-margin'))
+ .directive('layoutPadding', attributeWithoutValue('layout-padding'))
+ .directive('layoutWrap', attributeWithoutValue('layout-wrap'))
+ .directive('layoutNoWrap', attributeWithoutValue('layout-no-wrap'))
+ .directive('layoutFill', attributeWithoutValue('layout-fill'))
+
+ // !! Deprecated attributes: use the `-lt` (aka less-than) notations
+
+ .directive('layoutLtMd', warnAttrNotSupported('layout-lt-md', true))
+ .directive('layoutLtLg', warnAttrNotSupported('layout-lt-lg', true))
+ .directive('flexLtMd', warnAttrNotSupported('flex-lt-md', true))
+ .directive('flexLtLg', warnAttrNotSupported('flex-lt-lg', true))
+
+ .directive('layoutAlignLtMd', warnAttrNotSupported('layout-align-lt-md'))
+ .directive('layoutAlignLtLg', warnAttrNotSupported('layout-align-lt-lg'))
+ .directive('flexOrderLtMd', warnAttrNotSupported('flex-order-lt-md'))
+ .directive('flexOrderLtLg', warnAttrNotSupported('flex-order-lt-lg'))
+ .directive('offsetLtMd', warnAttrNotSupported('layout-offset-lt-md'))
+ .directive('offsetLtLg', warnAttrNotSupported('layout-offset-lt-lg'))
+
+ .directive('hideLtMd', warnAttrNotSupported('hide-lt-md'))
+ .directive('hideLtLg', warnAttrNotSupported('hide-lt-lg'))
+ .directive('showLtMd', warnAttrNotSupported('show-lt-md'))
+ .directive('showLtLg', warnAttrNotSupported('show-lt-lg'));
+
+ /**
+ * These functions create registration functions for ngMaterial Layout attribute directives
+ * This provides easy translation to switch ngMaterial attribute selectors to
+ * CLASS selectors and directives; which has huge performance implications
+ * for IE Browsers
+ */
+
+ /**
+ * Creates a directive registration function where a possible dynamic attribute
+ * value will be observed/watched.
+ * @param {string} className attribute name; eg `layout-gt-md` with value ="row"
+ */
+ function attributeWithObserve(className) {
+
+ return ['$mdUtil', '$$mdLayout', '$interpolate', function(_$mdUtil_, _$$mdLayout_, _$interpolate_) {
+ $mdUtil = _$mdUtil_;
+ $$mdLayout = _$$mdLayout_;
+ $interpolate = _$interpolate_;
+
+ return {
+ restrict: 'A',
+ compile: function(element, attr) {
+ var linkFn;
+ if (config.enabled) {
+ // immediately replace static (non-interpolated) invalid values...
+
+ validateAttributeValue( className,
+ getNormalizedAttrValue(className, attr, ""),
+ buildUpdateFn(element, className, attr)
+ );
+
+ linkFn = translateWithValueToCssClass;
+ }
- /**
- * These functions create registration functions for ngMaterial Layout attribute directives
- * This provides easy translation to switch ngMaterial attribute selectors to
- * CLASS selectors and directives; which has huge performance implications
- * for IE Browsers
- */
+ // Use for postLink to account for transforms after ng-transclude.
+ return linkFn || angular.noop;
+ }
+ };
+ }];
/**
- * Creates a directive registration function where a possbile dynamic attribute value will
- * be observed/watched.
- * @param {string} className attribute name; eg `md-layout-gt-md` with value ="row"
+ * Add as transformed class selector(s), then
+ * remove the deprecated attribute selector
*/
- function attributeWithObserve(className) {
-
- return ['$mdUtil', '$$mdLayout', '$document', '$parse', '$interpolate', function(_$mdUtil_, _$$mdLayout_, $document, _$parse_, _$interpolate_) {
- $mdUtil = _$mdUtil_;
- $$mdLayout = _$$mdLayout_;
- $parse = _$parse_;
- $interpolate = _$interpolate_;
+ function translateWithValueToCssClass(scope, element, attrs) {
+ var updateFn = updateClassWithValue(element, className, attrs);
+ var unwatch = attrs.$observe(attrs.$normalize(className), updateFn);
- return {
- restrict : 'A',
- compile: function(element, attr) {
- // Use for postLink to account for transforms after ng-transclude.
+ updateFn(getNormalizedAttrValue(className, attrs, ""));
+ scope.$on("$destroy", function() { unwatch() });
- if ( !injectLayoutSpecifier(element, attr) ) {
- attributeValueToClass(null, element, attr);
- return attributeValueToClass;
- }
+ if ($$mdLayout.removeAttributes) element.removeAttr(className);
+ }
+ }
+
+ /**
+ * Creates a registration function for ngMaterial Layout attribute directive.
+ * This is a `simple` transpose of attribute usage to class usage; where we ignore
+ * any attribute value
+ */
+ function attributeWithoutValue(className) {
+ return ['$$mdLayout', '$interpolate', function(_$$mdLayout_, _$interpolate_) {
+ $$mdLayout = _$$mdLayout_;
+ $interpolate = _$interpolate_;
+
+ return {
+ restrict: 'A',
+ compile: function(element, attr) {
+ var linkFn;
+ if (config.enabled) {
+ // immediately replace static (non-interpolated) invalid values...
+
+ validateAttributeValue( className,
+ getNormalizedAttrValue(className, attr, ""),
+ buildUpdateFn(element, className, attr)
+ );
+
+ translateToCssClass(null, element);
- return angular.noop;
- }
- };
- }];
-
- /**
- * To avoid large sets of CSS rules
- * for layout-gt-md-row, layout-sm-column, etc...
- *
- * Instead create either a md-layout-row or md-layout-column
- * class that acts as a generic specifier.
- *
- */
- function injectLayoutSpecifier(element, attrs) {
- var injected = false;
- var breakpoints = ['','-sm','-gt-sm','-md','-gt-md','-lg','-gt-lg'];
- angular.forEach(breakpoints, function(it){
- if ( className === "layout"+it ) {
-
- var updateClassFn = updateClassWithValue(element,"md-layout"+it, attrs);
- var normalizedAttr = attrs.$normalize(className);
- var attrValue = attrs[normalizedAttr] ? attrs[normalizedAttr].replace(/\s+/g, "-") : "row";
- var addImmediate = attrValue ? !needsInterpolation(attrValue) : false;
- var watchValue = needsInterpolation(attrValue);
-
-
- // Add special layout class: either '.md-layout-row' or '.md-layout-column'
- if ( addImmediate ) element.addClass( $mdUtil.supplant('md-layout{0}-{1}',[it,attrValue]) );
- if ( watchValue ) attrs.$observe( normalizedAttr, updateClassFn );
- if ( $$mdLayout.removeAttributes ) element.removeAttr(className);
-
- injected = true;
+ // Use for postLink to account for transforms after ng-transclude.
+ linkFn = translateToCssClass;
}
- });
-
- return injected;
- }
-
- /**
- * Add as transformed class selector(s), then
- * remove the deprecated attribute selector
- */
- function attributeValueToClass(scope, element, attrs) {
- var updateClassFn = updateClassWithValue(element,className, attrs);
- var normalizedAttr = attrs.$normalize(className);
- var attrValue = attrs[normalizedAttr] ? attrs[normalizedAttr].replace(/\s+/g, "-") : null;
- var addImmediate = attrValue ? !needsInterpolation(attrValue) : false;
- var watchValue = needsInterpolation(attrValue);
-
- // Add transformed class selector(s)
-
- if ( addImmediate ) element.addClass(className + "-" + attrValue);
- if ( watchValue ) attrs.$observe( normalizedAttr, updateClassFn );
- if ( !addImmediate && !watchValue ) element.addClass(className);
-
- if ( $$mdLayout.removeAttributes ) element.removeAttr(className);
- }
-
- }
- /**
- * See if the original value has interpolation symbols:
- * e.g. flex-gt-md="{{triggerPoint}}"
- */
- function needsInterpolation(value) {
- return (value ||"").indexOf($interpolate.startSymbol()) > -1;
- }
+ return linkFn || angular.noop;
+ }
+ };
+ }];
/**
- * After link-phase, do NOT remove deprecated layout attribute selector.
- * Instead watch the attribute so interpolated data-bindings to layout
- * selectors will continue to be supported.
- *
- * $observe() the className and update with new class (after removing the last one)
- *
- * e.g. `layout="{{layoutDemo.direction}}"` will update...
- *
- * NOTE: The value must match one of the specified styles in the CSS.
- * For example `flex-gt-md="{{size}}` where `scope.size == 47` will NOT work since
- * only breakpoints for 0, 5, 10, 15... 100, 33, 34, 66, 67 are defined.
- *
+ * Add as transformed class selector, then
+ * remove the deprecated attribute selector
*/
- function updateClassWithValue(element, className, attr) {
- var lastClass;
-
- return function updateClassWithValue(newValue) {
- var value = String(newValue || "").replace(/\s+/g, "-");
+ function translateToCssClass(scope, element) {
+ element.addClass(className);
+ if ($$mdLayout.removeAttributes) {
+ // After link-phase, remove deprecated layout attribute selector
+ element.removeAttr(className);
+ }
+ }
+ }
+
+
+
+ /**
+ * After link-phase, do NOT remove deprecated layout attribute selector.
+ * Instead watch the attribute so interpolated data-bindings to layout
+ * selectors will continue to be supported.
+ *
+ * $observe() the className and update with new class (after removing the last one)
+ *
+ * e.g. `layout="{{layoutDemo.direction}}"` will update...
+ *
+ * NOTE: The value must match one of the specified styles in the CSS.
+ * For example `flex-gt-md="{{size}}` where `scope.size == 47` will NOT work since
+ * only breakpoints for 0, 5, 10, 15... 100, 33, 34, 66, 67 are defined.
+ *
+ */
+ function updateClassWithValue(element, className) {
+ var lastClass;
+
+ return function updateClassFn(newValue) {
+ var value = validateAttributeValue(className, newValue || "");
+ if ( angular.isDefined(value) ) {
element.removeClass(lastClass);
- lastClass = !value ? className : className + "-" + value;
+ lastClass = !value ? className : className + "-" + value.replace(WHITESPACE, "-")
element.addClass(lastClass);
+ }
+ };
+ }
+
+ /**
+ * Provide console warning that this layout attribute has been deprecated
+ *
+ */
+ function warnAttrNotSupported(className) {
+ var parts = className.split("-");
+ return ["$log", function($log) {
+ $log.warn(className + "has been deprecated. Please use a `" + parts[0] + "-gt-
` variant.");
+ return angular.noop;
+ }];
+ }
+
+ /**
+ * For the Layout attribute value, validate or replace with default
+ * fallback value
+ */
+ function validateAttributeValue(className, value, updateFn) {
+ var origValue = value;
+
+ if (!needsInterpolation(value)) {
+ switch (className.replace(SUFFICES,"")) {
+ case 'layout' :
+ if ( !findIn(value, LAYOUT_OPTIONS) ) {
+ value = LAYOUT_OPTIONS[0]; // 'row';
+ }
+ break;
- // Conditionally remove the attribute selector in case the browser attempts to
- // read it and suffers a performance downgrade (IE).
-
- if ( $$mdLayout.removeAttributes ) element.removeAttr(className);
- };
- }
-
- /**
- * Creates a registration function with for ngMaterial Layout attribute directive.
- * This is a `simple` transpose of attribute usage to class usage
- */
- function attributeWithoutValue(className) {
- return ['$$mdLayout', '$document', function(_$$mdLayout_, $document) {
- $$mdLayout = _$$mdLayout_;
- return {
- restrict : 'A',
- compile: function(element, attrs) {
+ case 'flex' :
+ if (!findIn(value, FLEX_OPTIONS)) {
+ if (isNaN(+value)) {
+ value = '';
+ }
+ }
+ break;
- attributeToClass(null, element);
+ case 'layout-offset' :
+ case 'flex-order' :
+ if (!value || isNaN(+value)) {
+ value = '0';
+ }
+ break;
- // Use for postLink to account for transforms after ng-transclude.
- return attributeToClass;
+ case 'layout-align' :
+ if (!findIn(value, ALIGNMENT_OPTIONS, "-")) {
+ value = ALIGNMENT_OPTIONS[0]; // 'start-start';
}
- };
- }];
-
- /**
- * Add as transformed class selector, then
- * remove the deprecated attribute selector
- */
- function attributeToClass(scope, element) {
- element.addClass(className);
-
- if ( $$mdLayout.removeAttributes ) {
- // After link-phase, remove deprecated layout attribute selector
- element.removeAttr(className);
- }
+ break;
+
+ case 'layout-padding' :
+ case 'layout-margin' :
+ case 'layout-fill' :
+ case 'layout-wrap' :
+ case 'layout-no-wrap' :
+ value = '';
+ break;
}
- }
-
- /**
- * Provide console warning that this layout attribute has been deprecated
- *
- */
- function warnAttrNotSupported(className) {
- var parts = className.split("-");
- return ["$log", function($log) {
- $log.warn( className + "has been deprecated. Please use a `" + parts[0] + "-gt-` variant.");
- return angular.noop;
- }];
+ if (value != origValue) {
+ (updateFn || angular.noop)(value);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Replace current attribute value with fallback value
+ */
+ function buildUpdateFn(element, className, attrs) {
+ return function updateAttrValue(fallback) {
+ if (!needsInterpolation(fallback)) {
+ element.attr(className, fallback);
+ attrs[attrs.$normalize(className)] = fallback;
+ }
+ };
+ }
+
+ /**
+ * See if the original value has interpolation symbols:
+ * e.g. flex-gt-md="{{triggerPoint}}"
+ */
+ function needsInterpolation(value) {
+ return (value || "").indexOf($interpolate.startSymbol()) > -1;
+ }
+
+ function getNormalizedAttrValue(className, attrs, defaultVal) {
+ var normalizedAttr = attrs.$normalize(className);
+ return attrs[normalizedAttr] ? attrs[normalizedAttr].replace(WHITESPACE, "-") : defaultVal || null;
+ }
+
+ function findIn(item, list, replaceWith) {
+ item = replaceWith && item ? item.replace(WHITESPACE, replaceWith) : item;
+
+ var found = false;
+ if (item) {
+ list.forEach(function(it) {
+ it = replaceWith ? it.replace(WHITESPACE, replaceWith) : it;
+ found = found || (it === item);
+ });
}
+ return found;
+ }
})();
diff --git a/src/core/services/layout/layout.scss b/src/core/services/layout/layout.scss
index 5d8f6f4bee2..037c612234a 100644
--- a/src/core/services/layout/layout.scss
+++ b/src/core/services/layout/layout.scss
@@ -11,7 +11,7 @@
*/
@-moz-document url-prefix() {
- .layout-fill, [layout-fill] {
+ .layout-fill {
margin: 0;
width: 100%;
min-height: 100%;
@@ -31,7 +31,7 @@
// }
//
- .flex-order, [flex-order] {
+ .flex-order {
order : 0;
}
}
@@ -44,11 +44,7 @@
@if $s != '' { $suffix : '#{$s}-#{$i}'; }
@else { $suffix : '#{$i}'; }
- $order : $order + '.flex-order-#{$suffix}, ';
- }
- @each $s in $sizes {
- @if ( $s != '' ) { $order : $order + '[flex-order-#{$s}="#{$i}"], '; }
- @else { $order : $order + '[flex-order="#{$i}"], '; }
+ $order : $order + '.flex-order-#{$suffix}';
}
// .flex-order-0, [order="0"] {
@@ -91,11 +87,7 @@
@if $s != '' { $suffix : '#{$s}-#{$i * 5}'; }
@else { $suffix : '#{$i * 5}'; }
- $offsets : $offsets + '.offset-#{$suffix}, ';
- }
- @each $s in $sizes {
- @if ( $s != '' ) { $offsets : $offsets + '[offset-#{$s}="#{$i * 5}"], '; }
- @else { $offsets : $offsets + '[offset="#{$i * 5}"], '; }
+ $offsets : $offsets + '.offset-#{$suffix}';
}
#{$offsets} {
@@ -111,12 +103,7 @@
@if $s != '' { $suffix : '#{$s}-#{$i}'; }
@else { $suffix : '#{$i}'; }
- $offsets : $offsets + '.offset-#{$suffix}, ';
- }
-
- // add attribute selectors
- @each $s in $sizes {
- $offsets : $offsets + '[offset-#{$s}="#{$i}"], ';
+ $offsets : $offsets + '.offset-#{$suffix}';
}
}
@@ -136,12 +123,7 @@
@if $s != '' { $suffix : '#{$s}-#{$i}'; }
@else { $suffix : '#{$i}'; }
- $offsets : $offsets + '.offset-#{$suffix}, ';
- }
-
- // add attribute selectors
- @each $s in $sizes {
- $offsets : $offsets + '[offset-#{$s}="#{$i}"], ';
+ $offsets : $offsets + '.offset-#{$suffix}';
}
}
@@ -158,7 +140,7 @@
@if $name == null { $name : ''; }
@if $name != '' { $name : '-#{$name}'; }
- .md-layout#{$name}, .md-layout#{$name}-column, .md-layout#{$name}-row, {
+ .layout#{$name}, .layout#{$name}-column, .layout#{$name}-row, {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
@@ -166,8 +148,8 @@
display: -ms-flexbox;
display: flex;
}
- .md-layout#{$name}-column { flex-direction: column; }
- .md-layout#{$name}-row { flex-direction: row; }
+ .layout#{$name}-column { flex-direction: column; }
+ .layout#{$name}-row { flex-direction: row; }
}
@mixin flex-properties-for-name($name: null) {
$flexName: 'flex';
@@ -178,36 +160,51 @@
$name : '';
}
- .#{$flexName} {
- box-sizing: border-box;
- }
-
- .#{$flexName} { flex: 1; } // === 1 1 0%
- .#{$flexName}-grow { flex: 1 1 100%; }
- .#{$flexName}-initial { flex: 0 1 auto; }
- .#{$flexName}-auto { flex: 1 1 auto; }
- .#{$flexName}-none { flex: 0 0 auto; }
+ .#{$flexName} { flex: 1; box-sizing: border-box; } // === flex: 1 1 0%;
+ .#{$flexName}-grow { flex: 1 1 100%; box-sizing: border-box; }
+ .#{$flexName}-initial { flex: 0 1 auto; box-sizing: border-box; }
+ .#{$flexName}-auto { flex: 1 1 auto; box-sizing: border-box; }
+ .#{$flexName}-none { flex: 0 0 auto; box-sizing: border-box; }
// (1-20) * 5 = 0-100%
@for $i from 0 through 20 {
$value : #{$i * 5 + '%'};
- .#{$flexName}-#{$i * 5} { box-sizing: border-box; flex: 0 0 #{$value}; }
+ .#{$flexName}-#{$i * 5} {
+ flex: 0 0 #{$value};
+ max-width: #{$value};
+ max-height: 100%;
+ box-sizing: border-box;
+ }
- .md-layout-row > .#{$flexName}-#{$i * 5},
- .md-layout#{$name}-row > .#{$flexName}-#{$i * 5} { flex: 0 0 #{$value}; max-width: #{$value}; max-height: 100%; }
+ .layout-row > .#{$flexName}-#{$i * 5},
+ .layout#{$name}-row > .#{$flexName}-#{$i * 5} {
+ flex: 0 0 #{$value};
+ max-width: #{$value};
+ max-height: 100%;
+ box-sizing: border-box;
+ }
- .md-layout-column > .#{$flexName}-#{$i * 5},
- .md-layout#{$name}-column > .#{$flexName}-#{$i * 5} { flex: 0 0 #{$value}; max-width: 100%; max-height: #{$value}; }
+ .layout-column > .#{$flexName}-#{$i * 5},
+ .layout#{$name}-column > .#{$flexName}-#{$i * 5} {
+ flex: 0 0 #{$value};
+ max-width: 100%;
+ max-height: #{$value};
+ box-sizing: border-box;
+ }
}
- .md-layout-row, .md-layout#{$name}-row {
- > .#{$flexName}-33 , > .#{$flexName}-34 { flex: 0 0 33%; max-width: 33%; max-height: 100%; }
- > .#{$flexName}-66 , > .#{$flexName}-67 { flex: 0 0 67%; max-width: 67%; max-height: 100%; }
+ .layout-row, .layout#{$name}-row {
+ > .#{$flexName}-33 , > .#{$flexName}-33 { flex: 0 0 33%; max-width: 33%; max-height: 100%; box-sizing: border-box; }
+ > .#{$flexName}-34 , > .#{$flexName}-34 { flex: 0 0 34%; max-width: 34%; max-height: 100%; box-sizing: border-box; }
+ > .#{$flexName}-66 , > .#{$flexName}-66 { flex: 0 0 66%; max-width: 66%; max-height: 100%; box-sizing: border-box; }
+ > .#{$flexName}-67 , > .#{$flexName}-67 { flex: 0 0 67%; max-width: 67%; max-height: 100%; box-sizing: border-box; }
}
- .md-layout-column, .md-layout#{$name}-column {
- > .#{$flexName}-33 , > .#{$flexName}-34 { flex: 0 0 33%; max-width: 100%; max-height: 33%; }
- > .#{$flexName}-66 , > .#{$flexName}-67 { flex: 0 0 67%; max-width: 100%; max-height: 67%; }
+ .layout-column, .layout#{$name}-column {
+ > .#{$flexName}-33 , > .#{$flexName}-33 { flex: 0 0 33%; max-width: 100%; max-height: 33%; box-sizing: border-box; }
+ > .#{$flexName}-34 , > .#{$flexName}-34 { flex: 0 0 34%; max-width: 100%; max-height: 34%; box-sizing: border-box; }
+ > .#{$flexName}-66 , > .#{$flexName}-66 { flex: 0 0 66%; max-width: 100%; max-height: 66%; box-sizing: border-box; }
+ > .#{$flexName}-67 , > .#{$flexName}-67 { flex: 0 0 67%; max-width: 100%; max-height: 67%; box-sizing: border-box; }
}
}
@@ -338,15 +335,15 @@
margin: $layout-gutter-width / 1;
}
- .layout-wrap, [layout-wrap] {
+ .layout-wrap {
flex-wrap: wrap;
}
- .layout-nowrap, [layout-nowrap] {
+ .layout-nowrap {
flex-wrap: nowrap;
}
- .layout-fill, [layout-fill] {
+ .layout-fill {
margin: 0;
width: 100%;
min-height: 100%;
diff --git a/src/core/services/layout/layout.spec.js b/src/core/services/layout/layout.spec.js
index 5cfdbf136f7..f26f36021d6 100644
--- a/src/core/services/layout/layout.spec.js
+++ b/src/core/services/layout/layout.spec.js
@@ -1,125 +1,88 @@
describe('layout directives', function() {
+ var suffixes = ['sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg'];
+
beforeEach(module('material.core', 'material.core.layout'));
- describe('translated to layout classes', function() {
+ describe('using [layout] attributes', function() {
- var suffixes = ['sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg'];
- var directionValues = ['row', 'column'];
- var flexOrderValues = [-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
- var flexValues = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 33, 34, 66, 67];
- var offsetValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 33, 34, 66, 67];
- var alignmentValues = [
- "center", "center center", "center start", "center end",
- "end", "end center", "end start", "end end",
- "space-around", "space-around center", "space-around start", "space-around end",
- "space-between", "space-between center", "space-between start", "space-between end",
- "start center", "start start", "start end"];
- var mappings = [
- { attribute: 'flex', suffixes: suffixes, values: flexValues, testStandAlone: true},
- { attribute: 'flex-order', suffixes: suffixes, values: flexOrderValues },
- { attribute: 'offset', suffixes: suffixes, values: offsetValues },
- { attribute: 'hide', suffixes: suffixes, testStandAlone: true },
- { attribute: 'show', suffixes: suffixes, testStandAlone: true },
- { attribute: 'layout-align', suffixes: suffixes, values: alignmentValues },
- { attribute: 'layout-padding', testStandAlone: true },
- { attribute: 'layout-margin', testStandAlone: true },
- { attribute: 'layout-wrap', testStandAlone: true },
- { attribute: 'layout-fill', testStandAlone: true }
- ];
+ it("should support attribute without value ''", inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("layout")).toBeFalsy();
+ expect(element.hasClass("layout-row")).toBeTruthy();
+ }));
- // Run all the tests; iterating the mappings...
- testWithSuffix('layout', suffixes, directionValues);
+ it('should ignore invalid values', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("layout-row")).toBeTruthy();
+ expect(element.hasClass('layout-humpty')).toBeFalsy();
+ }));
- for (var i = 0; i < mappings.length; i++) {
- var map = mappings[i];
+ it('should support interpolated values layout-gt-sm="{{direction}}"', inject(function($compile, $rootScope) {
+ var scope = $rootScope.$new(),
+ element = $compile('
Layout
')(scope);
- if (map.testStandAlone) testSimpleDirective(map.attribute);
- if (map.values) testWithSuffixAndValue(map.attribute, map.values, undefined );
- if (map.suffixes) testWithSuffix(map.attribute, map.suffixes, map.values, map.testStandAlone );
- }
+ scope.$apply('direction = "row"');
+ expect(element.hasClass('layout-gt-sm-row')).toBeTruthy();
+ scope.$apply('direction = undefined');
+ expect(element.hasClass('layout-gt-sm-row')).toBeTruthy();
- /** Test a simple layout directive to validate that the layout class is added. */
- function testSimpleDirective(attribute, expectedClass) {
- // default fallback is attribute as class...
- expectedClass = expectedClass || attribute;
+ scope.$apply('direction = "column"');
+ expect(element.hasClass('layout-gt-sm-column')).toBeTruthy();
+ }));
- it('should fail if the class ' + expectedClass + ' was not added for attribute ' + attribute, inject(function($compile, $rootScope) {
- var element = $compile('
Layout
')($rootScope.$new());
- expect(element.hasClass(expectedClass)).toBe(true);
- }));
- }
+ /**
+ * For all breakpoints,
+ * - Test percentage values
+ * - Test valid non-numerics
+ *
+ * NOTE: include the '' suffix: layout='' === layout-row
+ */
+ var directionValues = ['row', 'column'];
- /** Test directives with 'sm', 'gt-sm', 'md', 'gt-md', 'lg', and 'gt-lg' suffixes */
- function testWithSuffixAndValue(attribute, values, suffix) {
- for (var j = 0; j < values.length; j++) {
- var value = values[j].toString();
- var attr = suffix ? attribute + '-' + suffix : attribute;
+ angular.forEach(directionValues, function(direction) {
+ angular.forEach([''].concat(suffixes), function(suffix) {
+ var className = suffix ? 'layout-' + suffix : 'layout';
+ testWithValue(className, direction);
+ });
+ });
- var attrWithValue = buildAttributeWithValue(attr, value);
- var expectedClass = buildExpectedClass(attr, value);
+ });
+ describe('using [flex] attributes', function() {
+ var allowedValues = [
+ 'grow', 'initial', 'auto', 'none',
+ 0, 5, 10, 15, 20, 25,
+ 30, 33, 34, 35, 40, 45,
+ 50, 55, 60, 65, 66, 67,
+ 70, 75, 80, 85, 90, 95, 100
+ ];
- // Run each test.
- testSimpleDirective(attrWithValue, expectedClass);
- }
+ it('should support attribute without value "
"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("flex")).toBeTruthy();
+ expect(element.hasClass("flex-flex")).toBeFalsy();
+ }));
- /**
- * Build string of expected classes that should be added to the DOM element.
- *
- * Convert directive with value to classes
- *
- * @param {string} attrClass Full attribute name; eg 'layout-gt-lg'
- * @param {string} attrValue HTML directive; eg "column"
- *
- * @returns {string} Class name(s) to be added; e.g., `layout-gt-lg-column`.
- */
- function buildExpectedClass(attrClass, attrValue) {
-
- // Layout attributes have special md-layout prefix class names
- angular.forEach([''].concat(suffixes), function(it){
- var layout = (it ? "layout-" : "layout") + it;
- if (attrClass == layout) attrClass = "md-" + attrClass;
- });
-
- return attrClass + "-" + attrValue.replace(/\s+/g, "-");
- }
+ it('should ignore invalid values non-numericals like flex="flex"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("flex")).toBeTruthy();
+ expect(element.hasClass('flex-flex')).toBeFalsy();
+ }));
- /**
- * Build full string of expected directive with its value
- * Note: The expected class always starts with the
- * attribute name, add the suffix if any.
- *
- * @param {string} attrClass Full attribute name; eg 'layout-gt-lg'
- * @param {string} attrValue HTML directive; eg "column"
- *
- * @returns {string} Attribute with value, e.g., `layout-gt-lg="column"`
- */
- function buildAttributeWithValue(attrClass, attrValue) {
- return attrClass + '="' + attrValue + '"';
- }
- }
+ it('should support interpolated values flex-gt-sm="{{columnSize}}"', inject(function($compile, $rootScope) {
+ var scope = $rootScope.$new(),
+ element = $compile('
Layout
')(scope);
- /**
- * Test directive as simple with media suffix and with associated values.
- * E.g., layout-gt-md="row"
- */
- function testWithSuffix(attribute, suffixes, values, testStandAlone) {
- for (var j = 0; j < suffixes.length; j++) {
- var suffix = suffixes[j];
- var attr = attribute + '-' + suffix;
+ scope.$apply('columnSize = 33');
+ expect(element.hasClass('flex-gt-sm-33')).toBeTruthy();
- if (testStandAlone) testSimpleDirective(attr);
- if (values) testWithSuffixAndValue(attribute, values, suffix);
- }
- }
- });
+ scope.$apply('columnSize = undefined');
+ expect(element.hasClass('flex-gt-sm')).toBeTruthy();
- describe('layout attribute with dynamic values', function() {
+ }));
it('should observe the attribute value and update the layout class(es)', inject(function($rootScope, $compile) {
var scope = $rootScope.$new();
- scope.size = undefined;
-
var element = angular.element($compile('
')(scope));
expect(element.hasClass('flex-gt-md')).toBe(true);
@@ -132,13 +95,235 @@ describe('layout directives', function() {
expect(element.hasClass('flex-gt-md-32')).toBe(true);
scope.$apply(function() {
+ // This should be rejected/ignored and the fallback "" value used
scope.size = "fishCheeks";
});
- expect(element.hasClass('flex-gt-md-32')).toBe(false);
- expect(element.hasClass('flex-gt-md-fishCheeks')).toBe(true);
+ expect(element.hasClass('flex-gt-md')).toBe(true);
+ expect(element.hasClass('flex-gt-md-fishCheeks')).toBe(false);
+
+ }));
+
+ testAllSufficesWithValues("flex", allowedValues );
+
+ });
+ describe('using [flex-order] attributes', function() {
+ var flexOrderValues = [
+ -9, -8, -7, -6, -5, -4, -3, -2, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
+ ];
+ it('should support attribute without value "
"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("flex-order-0")).toBeTruthy();
+ expect(element.hasClass("flex-order")).toBeFalsy();
}));
- })
+ it('should ignore invalid values non-numericals like flex-order="humpty"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("flex-order-0")).toBeTruthy();
+ expect(element.hasClass('flex-order-humpty')).toBeFalsy();
+ }));
+
+ it('should support interpolated values flex-order-gt-sm="{{index}}"', inject(function($compile, $rootScope) {
+ var scope = $rootScope.$new(),
+ element = $compile('
Layout
')(scope);
+
+ scope.$apply('index = 3');
+ expect(element.hasClass('flex-order-gt-sm-3')).toBeTruthy();
+
+ }));
+
+ testAllSufficesWithValues("flex-order", flexOrderValues );
+
+ });
+ describe('using [layout-offset] attributes', function() {
+ var offsetValues = [
+ 5, 10, 15, 20, 25,
+ 30, 35, 40, 45, 50,
+ 55, 60, 65, 70, 75,
+ 80, 85, 90, 95,
+ 33, 34, 66, 67
+ ];
+
+ it('should support attribute without value "
"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("layout-offset-0")).toBeTruthy();
+ expect(element.hasClass("layout-offset")).toBeFalsy();
+ }));
+
+ it('should ignore invalid values non-numericals like layout-offset="humpty"', inject(function($compile, $rootScope) {
+ var element = $compile('
Layout
')($rootScope.$new());
+ expect(element.hasClass("layout-offset-0")).toBeTruthy();
+ expect(element.hasClass('layout-offset-humpty')).toBeFalsy();
+ }));
+
+ it('should support interpolated values layout-offset-gt-sm="{{padding}}"', inject(function($compile, $rootScope) {
+ var scope = $rootScope.$new(),
+ element = $compile('
Layout
')(scope);
+
+ scope.$apply('padding = 15');
+ expect(element.hasClass('layout-offset-gt-sm-15')).toBeTruthy();
+ }));
+
+ testAllSufficesWithValues("layout-offset", offsetValues );
+
+ });
+ describe('using [layout-align] attributes', function() {
+ var attrName = "layout-align";
+ var alignmentValues = [
+ "center", "center center", "center start", "center end",
+ "end", "end center", "end start", "end end",
+ "space-around", "space-around center", "space-around start", "space-around end",
+ "space-between", "space-between center", "space-between start", "space-between end",
+ "start center", "start start", "start end"
+ ];
+
+ it('should support attribute without value "
"', inject(function($compile, $rootScope, $mdUtil) {
+ var markup = $mdUtil.supplant('
Layout
', [attrName]);
+ var element = $compile(markup)($rootScope.$new());
+
+ expect(element.hasClass(attrName + "-start-start")).toBeTruthy();
+ expect(element.hasClass(attrName)).toBeFalsy();
+ }));
+
+ it('should ignore invalid values non-numericals like layout-align="humpty"', inject(function($compile, $rootScope, $mdUtil) {
+ var markup = $mdUtil.supplant('
Layout
', [attrName]);
+ var element = $compile(markup)($rootScope.$new());
+
+ expect(element.hasClass(attrName + "-start-start")).toBeTruthy();
+ expect(element.hasClass(attrName + '-humpty')).toBeFalsy();
+ }));
+
+ it('should support interpolated values layout-align-gt-sm="{{alignItems}}"', inject(function($compile, $rootScope, $mdUtil) {
+ var scope = $rootScope.$new(),
+ markup = $mdUtil.supplant('
Layout
', [attrName]),
+ element = $compile(markup)(scope);
+
+ scope.$apply('alignItems = "center center"');
+ expect(element.hasClass(attrName + '-gt-sm-center-center')).toBeTruthy();
+ }));
+
+ testAllSufficesWithValues(attrName, alignmentValues );
+
+
+ });
+ describe('using [layout-] padding, fill, margin, wrap, and nowrap attributes', function() {
+ var allowedAttrsNoValues = [
+ "layout-padding",
+ "layout-margin",
+ "layout-fill",
+ "layout-wrap",
+ "layout-no-wrap"
+ ];
+
+ angular.forEach(allowedAttrsNoValues,function(name){
+ testNoValueAllowed(name);
+ })
+ });
+ describe('using [hide] attributes', function() {
+ var attrName = "hide",
+ breakpoints = [''].concat(suffixes);
+
+ angular.forEach(breakpoints, function(suffix) {
+ var className = suffix ? attrName + "-" + suffix : attrName;
+ testNoValueAllowed( className );
+ });
+
+ });
+ describe('using [show] attributes', function() {
+ var attrName = "show",
+ breakpoints = [''].concat(suffixes);
+
+ angular.forEach(breakpoints, function(suffix) {
+ var className = suffix ? attrName + "-" + suffix : attrName;
+ testNoValueAllowed( className );
+ });
+
+ });
+
+ // *****************************************************************
+ // Internal Test methods for the angular.forEach( ) loops
+ // *****************************************************************
+
+ /**
+ * For the specified attrName (e.g. flex) test all breakpoints
+ * with all allowed values.
+ */
+ function testAllSufficesWithValues(attrName, allowedValues) {
+ var breakpoints = [''].concat(suffixes);
+
+ angular.forEach(breakpoints, function(suffix) {
+ angular.forEach(allowedValues, function(value) {
+ var className = suffix ? attrName + "-" + suffix : attrName;
+ testWithValue(className, value, attrName );
+ });
+ });
+
+ }
+
+ /**
+ * Test other Layout directives (e.g. flex, flex-order, layout-offset)
+ */
+ function testWithValue(className, value, raw) {
+ var title = 'should allow valid values `' + className + '=' + value + '`';
+
+ it(title, inject(function($compile, $rootScope, $mdUtil) {
+
+ var expected = $mdUtil.supplant('{0}-{1}',[className, value ? String(value).replace(/\s+/g, "-") : value]);
+ var markup = $mdUtil.supplant('
Layout
',[className,value]);
+
+ var element = $compile(markup)($rootScope.$new());
+ expect(element.hasClass(expected)).toBeTruthy();
+
+ if ( raw ) {
+ // Is the raw value also present?
+ expect(element.hasClass(raw)).toBeFalsy();
+ }
+
+ }));
+ }
+
+ /**
+ * Layout directives do NOT support values nor breakpoint usages:
+ *
+ * - layout-margin,
+ * - layout-padding,
+ * - layout-fill,
+ * - layout-wrap,
+ * - layout-nowrap
+ *
+ */
+ function testNoValueAllowed(attrName) {
+
+ it('should support attribute without value "
"', inject(function($compile, $rootScope, $mdUtil) {
+ var markup = $mdUtil.supplant('
Layout
', [attrName]);
+ var element = $compile(markup)($rootScope.$new());
+
+ expect(element.hasClass(attrName)).toBeTruthy();
+ }));
+
+ it('should ignore invalid values non-numericals like '+ attrName +'="humpty"', inject(function($compile, $rootScope, $mdUtil) {
+ var markup = $mdUtil.supplant('
Layout
', [attrName]);
+ var element = $compile(markup)($rootScope.$new());
+
+ expect(element.hasClass(attrName)).toBeTruthy();
+ expect(element.hasClass(attrName + '-humpty')).toBeFalsy();
+ }));
+
+ it('should ignore interpolated values '+ attrName +'="{{someVal}}"', inject(function($compile, $rootScope, $mdUtil) {
+ var scope = $rootScope.$new(),
+ markup = $mdUtil.supplant('
Layout
', [attrName]),
+ element = $compile(markup)(scope);
+
+ scope.$apply('someVal = "30"');
+
+ expect(element.hasClass(attrName)).toBeTruthy();
+ expect(element.hasClass( $mdUtil.supplant("{0}-30",[attrName]) )).toBeFalsy();
+
+ }));
+ }
+
+
+
});