diff --git a/src/core/core.js b/src/core/core.js index 7a200adb4b5..10d8402e03c 100644 --- a/src/core/core.js +++ b/src/core/core.js @@ -7,6 +7,7 @@ angular 'ngAnimate', 'material.animate', 'material.core.gestures', + 'material.core.layout', 'material.core.theming' ]) .directive('mdTemplate', MdTemplateDirective) diff --git a/src/core/services/layout/layouts.js b/src/core/services/layout/layout.js similarity index 84% rename from src/core/services/layout/layouts.js rename to src/core/services/layout/layout.js index 061b6baba55..7195c700cfb 100644 --- a/src/core/services/layout/layouts.js +++ b/src/core/services/layout/layout.js @@ -2,7 +2,46 @@ 'use strict'; - angular.module('material.layouts', ['material.core']) + /** + * + * The original ngMaterial Layout solution used attribute selectors and CSS. + * + * ```html + *
My Content
+ * ``` + * + * ```css + * [layout] { + * box-sizing: border-box; + * display:flex; + * } + * [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 { + * box-sizing: border-box; + * display:flex; + * } + * .layout-column { + * flex-direction : column + * } + * ``` + */ + angular.module('material.core.layout', [ ]) // Attribute directives with optional value(s) diff --git a/src/core/services/layout/layout.spec.js b/src/core/services/layout/layout.spec.js new file mode 100644 index 00000000000..989ee860de0 --- /dev/null +++ b/src/core/services/layout/layout.spec.js @@ -0,0 +1,118 @@ +describe('layout directives', function() { + + beforeEach(module('material.core')); + + describe('expecting layout classes', function() { + + var suffixes = ['sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg']; + + var directionValues = ['row', 'column']; + var flexOrderValues = [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', + 'center center', 'start center', 'end center', 'space-between center', 'space-around center', + 'center start', 'start start', 'end start', 'space-between start', 'space-around start', + 'center end', 'start end', 'end end', 'space-between end', 'space-around end' + ]; + + var mappings = [ + { attribute: 'layout', suffixes: suffixes, values: directionValues, addDirectiveAsClass: true, 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 }, + { attribute: 'flex', suffixes: suffixes, values: flexValues, addDirectiveAsClass: true, 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 } + ]; + + // Run all the tests; iterating the mappings... + + for (var i = 0; i < mappings.length; i++) { + var map = mappings[i]; + + if (map.testStandAlone) testMapping(map.attribute, map.attribute); + if (map.suffixes) testWithSuffix(map.attribute, map.suffixes, map.values, map.testStandAlone, map.addDirectiveAsClass); + if (map.values) testWithSuffixAndValue(map.attribute, map.values, undefined, map.addDirectiveAsClass); + } + + /** + * + */ + function testMapping(attribute, expectedClass) { + it('should fail if the class ' + expectedClass + ' was not added for attribute ' + attribute, inject(function($compile, $rootScope) { + var element = $compile('
Layout
')($rootScope); + expect(element.hasClass(expectedClass)).toBe(true); + })); + } + + /** + * + */ + function testWithSuffixAndValue(attribute, values, suffix, addDirectiveAsClass) { + + for (var j = 0; j < values.length; j++) { + var value = values[j].toString(); + var attr = suffix ? attribute + '-' + suffix : attribute; + + var attrWithValue = buildAttributeWithValue(attr, value); + var expectedClass = buildExpectedClass(attr, value); + + // Run each test. + testMapping(attrWithValue, expectedClass); + } + + /** + * Build string of expected classes that should be added to the + * DOM element. + * + * Convert directive with value to classes + * + * @param attrClass String full attribute name; eg 'layout-gt-lg' + * @param attrValue String HTML directive; eg "column" + * + * @returns String to be used with element.addClass(...); eg `layout-gt-lg layout-gt-lg-column` + */ + function buildExpectedClass(attrClass, attrValue) { + if (addDirectiveAsClass) attrClass += ' ' + attrClass; + return attrClass + "-" + attrValue.replace(/\s+/g, "-"); + } + + /** + * 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 attrClass String full attribute name; eg 'layout-gt-lg' + * @param attrValue String HTML directive; eg "column" + * + * @returns String like `layout-gt-lg="column"` + */ + function buildAttributeWithValue(attr, value) { + return attr + '="' + value + '"'; + } + } + + /** + * + */ + function testWithSuffix(attribute, suffixes, values, testStandAlone, addDirectiveAsClass) { + for (var j = 0; j < suffixes.length; j++) { + var suffix = suffixes[j]; + var attr = attribute + '-' + suffix; + + if (testStandAlone) testMapping(attr, attr); + if (values) testWithSuffixAndValue(attribute, values, suffix, addDirectiveAsClass); + } + } + }); + +}); diff --git a/src/core/services/layout/layouts.spec.js b/src/core/services/layout/layouts.spec.js deleted file mode 100644 index 3feffbfef70..00000000000 --- a/src/core/services/layout/layouts.spec.js +++ /dev/null @@ -1,87 +0,0 @@ -describe('layouts service', function () { - beforeEach(module('material.layouts')); - - describe('expecting layout classes', function () { - - var suffixes = ['sm', 'gt-sm', 'md', 'gt-md', 'lg', 'gt-lg']; - - var directionValues = ['row', 'column']; - 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 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', - 'center center', 'start center', 'end center', 'space-between center', 'space-around center', - 'center start', 'start start', 'end start', 'space-between start', 'space-around start', - 'center end', 'start end', 'end end', 'space-between end', 'space-around end']; - var flexOrderValues = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - 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 mappings = [{ attribute: 'layout', suffixes: suffixes, values: directionValues, testStandAlone: true, addDirectiveAsClass: true }, - { attribute: 'flex', suffixes: suffixes, values: flexValues, testStandAlone: true, addDirectiveAsClass: true }, - { attribute: 'layout-align', suffixes: suffixes, values: alignmentValues }, - { attribute: 'flex-order', suffixes: suffixes, values: flexOrderValues }, - { attribute: 'offset', suffixes: suffixes, values: offsetValues }, - { attribute: 'layout-padding', testStandAlone: true }, - { attribute: 'layout-margin', testStandAlone: true }, - { attribute: 'layout-wrap', testStandAlone: true }, - { attribute: 'layout-fill', testStandAlone: true }, - { attribute: 'hide', suffixes: suffixes, testStandAlone: true }, - { attribute: 'show', suffixes: suffixes, testStandAlone: true }]; - - function testMapping(attribute, expectedClass) { - it('should fail if the class ' + expectedClass + ' was not added for attribute ' + attribute, inject(function ($compile, $rootScope) { - var element = $compile('
Layout
')($rootScope); - expect(element.hasClass(expectedClass)).toBe(true); - })); - } - - function testWithSuffix(attribute, suffixes, values, testStandAlone, addDirectiveAsClass) { - for (var j = 0; j < suffixes.length; j++) { - var suffix = suffixes[j]; - // Add the suffix to the attribute. - var attributeWithValue = attribute + '-' + suffix; - // Add the suffix to the expected class. - var expectedClass = attribute + '-' + suffix; - // Run the test. - if (testStandAlone) - testMapping(attributeWithValue, expectedClass); - // Add suffixes with values. - if (values) - testWithSuffixAndValue(attribute, values, suffix, addDirectiveAsClass); - }; - } - - function testWithSuffixAndValue(attribute, values, suffix, addDirectiveAsClass) { - for (var j = 0; j < values.length; j++) { - var attributeValue = values[j].toString(); - // If there was a suffix passed in, add it first. - var attributeWithValue = suffix ? attribute + '-' + suffix : attribute; - // Add the value. - attributeWithValue += '="' + attributeValue + '"'; - // The expected class always starts with the attribute name, add the suffix if any. - var expectedClass = suffix ? attribute + '-' + suffix : attribute; - // Add the class if necessary. - if (addDirectiveAsClass) - expectedClass += ' ' + expectedClass; - // Add the attribute and suffix to the expected class and replace the spaces with a dash. - expectedClass += '-' + attributeValue.replace(/\s+/g, "-"); - // Run the test. - testMapping(attributeWithValue, expectedClass); - }; - } - - for (var i = 0; i < mappings.length; i++) { - var mapping = mappings[i]; - // First test the mapping without any suffixes or values. - if (mapping.testStandAlone) - testMapping(mapping.attribute, mapping.attribute); - // Check for suffixes. - if (mapping.suffixes) - testWithSuffix(mapping.attribute, mapping.suffixes, mapping.values, mapping.testStandAlone, mapping.addDirectiveAsClass); - // Check for values. - if (mapping.values) - testWithSuffixAndValue(mapping.attribute, mapping.values, undefined, mapping.addDirectiveAsClass); - } - }); -});