From 2a1de83bcd2aa41e969e54c0292a4819a44a3c5b Mon Sep 17 00:00:00 2001 From: Thomas Burleson Date: Wed, 2 Dec 2015 11:32:26 -0600 Subject: [PATCH] fix(layouts): register Layout directives for xs, gt-xs, xl * Refactor Layout API directive registration to iteration all known breakpoints. * Update `layout.spec.js` tests to test all breakpoints Fixes #5995. Fixes #5983. Closes #6037. --- src/core/services/layout/layout.js | 200 ++++++++++-------------- src/core/services/layout/layout.spec.js | 2 +- 2 files changed, 86 insertions(+), 116 deletions(-) diff --git a/src/core/services/layout/layout.js b/src/core/services/layout/layout.js index 992c0cb60ac..d6d4c899e0b 100644 --- a/src/core/services/layout/layout.js +++ b/src/core/services/layout/layout.js @@ -11,7 +11,6 @@ var ALIGNMENT_MAIN_AXIS= [ "", "start", "center", "end", "stretch", "space-around", "space-between" ]; var ALIGNMENT_CROSS_AXIS= [ "", "start", "center", "end", "stretch" ]; - var config = { /** * Enable directive attribute-to-class conversions @@ -31,7 +30,11 @@ breakpoints: [] }; + registerLayoutAPI( angular.module('material.core.layout', ['ng']) ); + /** + * registerLayoutAPI() + * * The original ngMaterial Layout solution used attribute selectors and CSS. * * ```html @@ -40,12 +43,12 @@ * * ```css * [layout] { - * box-sizing: border-box; - * display:flex; - * } + * box-sizing: border-box; + * display:flex; + * } * [layout=column] { - * flex-direction : column - * } + * flex-direction : column + * } * ``` * * Use of attribute selectors creates significant performance impacts in some @@ -61,117 +64,83 @@ * * ```css * .layout { - * box-sizing: border-box; - * display:flex; - * } + * box-sizing: border-box; + * display:flex; + * } * .layout-column { - * flex-direction : column - * } + * flex-direction : column + * } * ``` */ - angular.module('material.core.layout', ['ng']) - - .directive('mdLayoutCss', disableLayoutDirective ) - - .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('flexOffset', attributeWithObserve('flex-offset')) - .directive('flexOffsetSm', attributeWithObserve('flex-offset-sm')) - .directive('flexOffsetGtSm', attributeWithObserve('flex-offset-gt-sm')) - .directive('flexOffsetMd', attributeWithObserve('flex-offset-md')) - .directive('flexOffsetGtMd', attributeWithObserve('flex-offset-gt-md')) - .directive('flexOffsetLg', attributeWithObserve('flex-offset-lg')) - .directive('flexOffsetGtLg', attributeWithObserve('flex-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('layoutPadding', attributeWithoutValue('layout-padding')) - .directive('layoutPaddingSm', attributeWithoutValue('layout-padding-sm')) - .directive('layoutPaddingGtSm', attributeWithoutValue('layout-padding-gt-sm')) - .directive('layoutPaddingMd', attributeWithoutValue('layout-padding-md')) - .directive('layoutPaddingGtMd', attributeWithoutValue('layout-padding-gt-md')) - .directive('layoutPaddingLg', attributeWithoutValue('layout-padding-lg')) - .directive('layoutPaddingGtLg', attributeWithoutValue('layout-padding-gt-lg')) - - .directive('layoutMargin', attributeWithoutValue('layout-margin')) - .directive('layoutMarginSm', attributeWithoutValue('layout-margin-sm')) - .directive('layoutMarginGtSm', attributeWithoutValue('layout-margin-gt-sm')) - .directive('layoutMarginMd', attributeWithoutValue('layout-margin-md')) - .directive('layoutMarginGtMd', attributeWithoutValue('layout-margin-gt-md')) - .directive('layoutMarginLg', attributeWithoutValue('layout-margin-lg')) - .directive('layoutMarginGtLg', attributeWithoutValue('layout-margin-gt-lg')) - - .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('flex-offset-lt-md')) - .directive('offsetLtLg', warnAttrNotSupported('flex-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')) - - .directive('ngCloak', buildCloakInterceptor('ng-cloak')); + function registerLayoutAPI(module){ + var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; + var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; + + // NOTE: these are also defined in constants::MEDIA_PRIORITY and constants::MEDIA + var BREAKPOINTS = [ "", "xs", "gt-xs", "sm", "gt-sm", "md", "gt-md", "lg", "gt-lg", "xl" ]; + var API_WITH_VALUES = [ "layout", "flex", "flex-order", "flex-offset", "layout-align" ]; + var API_NO_VALUES = [ "show", "hide", "layout-padding", "layout-margin" ]; + + + // Build directive registration functions for the standard Layout API... for all breakpoints. + angular.forEach(BREAKPOINTS, function(mqb) { + + // Attribute directives with expected, observable value(s) + angular.forEach( API_WITH_VALUES, function(name){ + var fullName = mqb ? name + "-" + mqb : name; + module.directive( directiveNormalize(fullName), attributeWithObserve(fullName)); + }); + + // Attribute directives with no expected value(s) + angular.forEach( API_NO_VALUES, function(name){ + var fullName = mqb ? name + "-" + mqb : name; + module.directive( directiveNormalize(fullName), attributeWithoutValue(fullName)); + }); + + }); + + // Register other, special directive functions for the Layout features: + module + .directive('mdLayoutCss' , disableLayoutDirective ) + .directive('ngCloak' , buildCloakInterceptor('ng-cloak')) + + .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('flex-offset-lt-md')) + .directive('offsetLtLg' , warnAttrNotSupported('flex-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')); + + /** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ + function directiveNormalize(name) { + return name + .replace(PREFIX_REGEXP, '') + .replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }); + } + + } /** * Special directive that will disable ALL Layout conversions of layout @@ -464,7 +433,7 @@ cross: "stretch" }; - var values = (value || "").replace(WHITESPACE, "-").split("-"); + var values = (value || "").toLowerCase().trim().replace(WHITESPACE, "-").split("-"); if ( values.length == 3 ) { values = [ values[0]+"-"+values[1],values[2] ]; } @@ -478,4 +447,5 @@ return axis; } + })(); diff --git a/src/core/services/layout/layout.spec.js b/src/core/services/layout/layout.spec.js index 81a20004d7b..aee01b23a42 100644 --- a/src/core/services/layout/layout.spec.js +++ b/src/core/services/layout/layout.spec.js @@ -1,5 +1,5 @@ describe('layout directives', function() { - var suffixes = ['sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg'], + var suffixes = ['xs', 'gt-xs', 'sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg', 'xl'], $mdUtil, $compile, pageScope; beforeEach(module('material.core', 'material.core.layout'));