diff --git a/config/build.config.js b/config/build.config.js index 0527faadb86..4c32cad5ecc 100644 --- a/config/build.config.js +++ b/config/build.config.js @@ -77,6 +77,9 @@ module.exports = { 'src/components/animate/noEffect.js', 'src/components/animate/inkCssRipple.js', + // Sticky Components + 'src/components/sticky/sticky.js', + // Components 'src/components/buttons/buttons.js', 'src/components/card/card.js', @@ -90,6 +93,7 @@ module.exports = { 'src/components/sidenav/sidenav.js', 'src/components/slider/slider.js', 'src/components/switch/switch.js', + 'src/components/subheader/subheader.js', 'src/components/tabs/tabs.js', 'src/components/tabs/js/*.js', 'src/components/toast/toast.js', diff --git a/src/components/list/_list.scss b/src/components/list/_list.scss index 5723a1089c9..9d41f213cc0 100644 --- a/src/components/list/_list.scss +++ b/src/components/list/_list.scss @@ -1,5 +1,9 @@ material-list { + padding: $list-padding-top $list-padding-right $list-padding-bottom $list-padding-left; + &.with-subheader { + padding-top: 0px; + } } material-item { @@ -35,16 +39,16 @@ material-item-content { text-overflow: ellipsis; - h2 { - margin: $list-h2-margin; - font-weight: $list-h2-font-weight; - font-size: $list-h2-font-size; - } h3 { margin: $list-h3-margin; font-weight: $list-h3-font-weight; font-size: $list-h3-font-size; } + h4 { + margin: $list-h4-margin; + font-weight: $list-h4-font-weight; + font-size: $list-h4-font-size; + } p { margin: $list-p-margin; font-size: $list-p-font-size; diff --git a/src/components/list/demo1/index.html b/src/components/list/demo1/index.html index 455e6f59396..47ffe3e18d1 100644 --- a/src/components/list/demo1/index.html +++ b/src/components/list/demo1/index.html @@ -10,8 +10,8 @@ {{item.who}}
-

{{item.what}}

-

{{item.who}}

+

{{item.what}}

+

{{item.who}}

{{item.notes}}

diff --git a/src/components/list/list.js b/src/components/list/list.js index 6e56d8f505c..bf3912119c9 100644 --- a/src/components/list/list.js +++ b/src/components/list/list.js @@ -31,8 +31,8 @@ angular.module('material.components.list', []) * {{item.who}} *
*
- *

{{item.what}}

- *

{{item.who}}

+ *

{{item.what}}

+ *

{{item.who}}

*

* {{item.notes}} *

diff --git a/src/components/sticky/_sticky.scss b/src/components/sticky/_sticky.scss new file mode 100644 index 00000000000..68198ab6042 --- /dev/null +++ b/src/components/sticky/_sticky.scss @@ -0,0 +1,5 @@ +.material-sticky-active { + top: 0px; + position: fixed; + z-index: 2; +} diff --git a/src/components/sticky/sticky.js b/src/components/sticky/sticky.js new file mode 100644 index 00000000000..bb65ad8c1c7 --- /dev/null +++ b/src/components/sticky/sticky.js @@ -0,0 +1,312 @@ +/** + * @ngdoc module + * @name material.components.sticky + * @description + * + * Sticky effects for material + */ + +angular.module('material.components.sticky', [ + 'material.components.content', + 'material.decorators', + 'material.animations' +]) +.factory('$materialSticky', [ + '$window', + '$document', + '$$rAF', + '$materialEffects', + MaterialSticky +]) +.directive('materialSticky', [ + '$materialSticky', + MaterialStickyDirective +]); + +/** + * @ngdoc factory + * @name $materialSticky + * @module material.components.sticky + * + * @description + * The `$materialSticky`service provides a mixin to make elements sticky. + * + * @returns A `$materialSticky` function that takes `$el` as an argument. + */ + +function MaterialSticky($window, $document, $$rAF, $materialEffects) { + var browserStickySupport; + + /** + * Registers an element as sticky, used internally by directives to register themselves + */ + + + function registerStickyElement(scope, $el) { + scope.$on('$destroy', function() { $deregister($el); }); + $el = Util.wrap($el, 'div', 'sticky-container'); + + var ctrl = $el.controller('materialContent'); + + if (!ctrl) { throw new Error('$materialSticky used outside of material-content'); } + + var $container = ctrl.$element; + + /* + * The sticky object on the container stores everything we need. + * `elements`: all known sticky elements within the container + * `orderedElements`: elements, ordered by vertical position within the layout + * `check`: debounced function to check elements for adjustment on scroll + * `targetIndex`: the index in orderedElements of the currently active sticky el + */ + + var $sticky = $container.data('$sticky') || { + elements: [], // all known sticky elements within $container + orderedElements: [], // elements, ordered by vertical position in layout + check: $$rAF.debounce(angular.bind(undefined, checkElements, $container)), + targetIndex: 0 + }; + + $sticky.elements.push($el); + + // check sticky support on first register + if (browserStickySupport === undefined) { + browserStickySupport = checkStickySupport($el); + } else if (browserStickySupport) { + $el.css({position: browserStickySupport, top: '0px', 'z-index': 2}); + } + + if (!browserStickySupport) { + if ($sticky.elements.length == 1) { + $container.data('$sticky', $sticky); + $container.on('scroll', $sticky.check); + } + queueScan(); + } + + return $deregister; + + + // Deregister a sticky element, useful for $destroy event. + function $deregister($el) { + if ($deregister.called) return; + $deregister.called = true; + var innerElements = elements.map(function(el) { return el.children(0); }); + var index = innerElements.indexOf($el); + if (index !== -1) { + elements[index].replaceWith($el); + elements.splice(index, 1); + if (elements.length === 0) { + $container.off('scroll', $sticky.check); + $container.removeData('$sticky'); + } + } + } + + // Method that will scan the elements after the current digest cycle + function queueScan() { + if (!queueScan.queued) { + queueScan.queued = true; + scope.$$postDigest(function() { + scanElements($container); + queueScan.queued = false; + }); + } + } + } + return registerStickyElement; + + // Function to check for browser sticky support + + function checkStickySupport($el) { + var stickyProps = ['sticky', '-webkit-sticky']; + for (var i = 0; i < stickyProps.length; ++i) { + $el.css({position: stickyProps[i], top: 0, 'z-index': 2}); + if ($el.css('position') == stickyProps[i]) { + return stickyProps[i]; + } + } + $el.css({position: undefined, top: undefined}); + return false; + } + + + /** + * Function to prepare our lookups so we can go quick! + */ + function scanElements($container) { + if (browserStickySupport) return; + + var $sticky = $container.data('$sticky'); + + // Sort based on position in the window, and assign an active index + $sticky.orderedElements = $sticky.elements.sort(function(a, b) { + return rect(a).top - rect(b).top; + }); + + $sticky.targetIndex = findTargetElementIndex(); + + + // Iterate over our sorted elements and find the one that is active + function findTargetElementIndex() { + var scroll = $container.prop('scrollTop'); + for(var i = 0; i < $sticky.orderedElements.length ; ++i) { + if (rect($sticky.orderedElements[i]).bottom > 0) { + return i > 0 ? i - 1 : i; + } else { + return i; + } + } + } + } + + // Function that executes on scroll to see if we need to do adjustments + function checkElements($container) { + var next; // pointer to next target + + var $sticky = $container.data('$sticky'); + + var targetElementIndex = $sticky.targetIndex; + var orderedElements = $sticky.orderedElements; + + /* + * Since we wrap in an element (to keep track of where in the layout the + * element would normally be, we use children to get the actual sticky + * element. + */ + + var content = targetElement().children(0); + var contentRect = rect(content); + var containerRect = rect($container); + var targetRect = rect(targetElement()); + + var scrollingDown = false; + var currentScroll = $container.prop('scrollTop'); + var lastScroll = $sticky.lastScroll; + if (currentScroll > (lastScroll || 0)) { + scrollingDown = true; + } + $sticky.lastScroll = currentScroll; + + var stickyActive = content.hasClass('material-sticky-active'); + + + // If we are scrollingDown, sticky, and are being pushed off screen by a different element, increment + if (scrollingDown && stickyActive && contentRect.bottom <= containerRect.top && targetElementIndex < orderedElements.length - 1) { + targetElement().children(0).removeClass('material-sticky-active'); + targetElement().css('height', null); + incrementElement(); + return; + + //If we are going up, and our normal position would be rendered not sticky, un-sticky ourselves + } else if (!scrollingDown && stickyActive && targetRect.top > containerRect.top) { + targetElement().children(0).removeClass('material-sticky-active'); + targetElement().css('height', null); + if (targetElementIndex > 0) { + incrementElement(-1); + content.addClass('material-sticky-active'); + transformY(content, -contentRect.height); + targetElement().css('height', contentRect.height + 'px'); + return; + } + return; // explicit return for the blind + + /* + * If we are going off screen and haven't been made sticky yet, go sticky + * Check at 0 so that if we get lucky on the scroll position, we activate + * sticky and avoid floating off the top for a second + */ + + } else if (scrollingDown && contentRect.top <= containerRect.top && !stickyActive) { + content.addClass('material-sticky-active'); + targetElement().css('height', contentRect.height + 'px'); + contentRect = rect(content); + next = targetElement(+1); + var offset = 0; + if (next) { + nextRect = rect(next.children(0)); + if (rectsAreTouching(contentRect, nextRect)) { + offset = nextRect.top - contentRect.bottom; + } + transformY(content, Math.min(offset, 0)); + } + return; + } + + var nextRect, offsetAmount, currentTop, translateAmt; + + // check if we need to push + if (scrollingDown) { + next = targetElement(+1); + if (next) { + nextRect = rect(next.children(0)); + if (rectsAreTouching(contentRect, nextRect)) { + offsetAmount = contentRect.bottom - nextRect.top; + currentTop = transformY(content); + translateAmt = currentTop - offsetAmount; + transformY(content, translateAmt); + } + } + // Check if we need to pull + } else if (targetElementIndex < orderedElements.length - 1 && contentRect.top < containerRect.top) { + nextRect = rect(targetElement(+1).children(0)); + offsetAmount = contentRect.bottom - nextRect.top; + currentTop = transformY(content); + translateAmt = Math.min(currentTop - offsetAmount, 0); + transformY(content, translateAmt); + } + + function incrementElement(inc) { + inc = inc || 1; + targetElementIndex += inc; + content = targetElement().children(0); + contentRect = rect(content); + $sticky.targetIndex = targetElementIndex; + } + + function targetElement(indexModifier) { + indexModifier = indexModifier || 0; + if (targetElementIndex === undefined) return undefined; + return orderedElements[targetElementIndex + indexModifier]; + } + } + + function rectsAreTouching(first, second) { + return first.bottom >= second.top; + } + + // Helper functions to get position of element + + function rect($el) { + return $el.hasOwnProperty(0) ? $el[0].getBoundingClientRect() : $el.getBoundingClientRect(); + } + + // Getter / setter for transform + function transformY($el, amnt) { + if (amnt === undefined) { + return $el.data('translatedHeight') || 0; + } else { + $el.css($materialEffects.TRANSFORM, 'translate3d(0, ' + amnt + 'px, 0)'); + $el.data('translatedHeight', amnt); + } + } + + +} + +/** + * @ngdoc directive + * @name materialSticky + * @module material.components.sticky + * + * @description + * Directive to consume the $materialSticky service + * + * @returns A material-sticky directive + */ +function MaterialStickyDirective($materialSticky) { + return { + restrict: 'A', + link: $materialSticky + }; +} diff --git a/src/components/sticky/sticky.spec.js b/src/components/sticky/sticky.spec.js new file mode 100644 index 00000000000..7d13721cf4a --- /dev/null +++ b/src/components/sticky/sticky.spec.js @@ -0,0 +1,148 @@ +describe('$materialStickySpec', function() { + var $document, $compile, $rootScope, $materialSticky; + beforeEach(module('material.components.sticky', function($provide) { + var $$rAF = { debounce: function(fn) { return function() { fn(); }; }}; + $provide.value('$$rAF', $$rAF); + })); + + beforeEach(inject(function(_$document_, _$compile_, _$rootScope_, _$materialSticky_) { + $document = _$document_; + $rootScope = _$rootScope_; + $compile = _$compile_; + $materialSticky = _$materialSticky_; + })); + + var $container, $firstSticky, $secondSticky, $sticky; + function setup(opts) { + opts = opts || {}; + + var TEST_HTML = '

First sticky

Second sticky

'; + var scope = $rootScope.$new(); + $container = $compile(TEST_HTML)(scope); + $firstSticky = $container.children().eq(0); + $secondSticky = $container.children().eq(1); + + // Wire up our special $container instance; + $firstSticky.controller('materialContent').$element = $container; + + $document.find('body').html(''); + $document.find('body').append($container); + + + if(!opts.skipFirst) { + $materialSticky($rootScope.$new(), $firstSticky); + } + if(!opts.skipSecond) { + $materialSticky($rootScope.$new(), $secondSticky); + } + + + // Overwrite the scrollTop property to return the opts.containerScroll + if(opts.containerScroll) { + var originalProp = $container.prop; + $container.prop = function(prop) { + if(prop == 'scrollTop') { + return opts.containerScroll; + } else { + originalProp.call($container, prop); + } + }; + } + + // Overwrite children() to provide mock rect positions + if(opts.firstActual) { + $firstSticky[0].getBoundingClientRect = function() { return opts.firstActual; }; + } + if(opts.secondActual) { + $secondSticky[0].getBoundingClientRect = function() { return opts.secondActual; }; + } + if(opts.firstTarget) { + var $firstOuter = $firstSticky.parent(); + $firstOuter[0].getBoundingClientRect = function() { return opts.firstTarget; }; + } + if(opts.secondTarget) { + var $secondOuter = $secondSticky.parent(); + $secondOuter[0].getBoundingClientRect = function() { return opts.secondTarget; }; + } + + + + $sticky = $container.data('$sticky'); + + if(opts.lastScroll) { $sticky.lastScroll = opts.lastScroll; } + + scope.$digest(); + } + + it('throws an error if uses outside of material-content', inject(function($materialSticky, $compile, $rootScope) { + var html = '

Hello world!

'; + function useWithoutMaterialContent() { + $materialSticky($rootScope.$new(), angular.element(html)); + } + expect(useWithoutMaterialContent).toThrow('$materialSticky used outside of material-content'); + })); + + it('adds class material-sticky-active when an element would scroll off screen', function() { + var firstActual = { top: -10, bottom: 9, height: 19 }; + setup({containerScroll: 10, firstActual: firstActual, skipSecond: true}); + $sticky.check(); + expect($firstSticky.hasClass('material-sticky-active')).toBe(true); + }); + + it('removes class material-sticky-active when an element is no longer sticky', function() { + var firstTarget = { top: 1, bottom: 10, height: 9 }; + setup({ + containerScroll: 10, + lastScroll: 11 + }); + $firstSticky.addClass('material-sticky-active'); + $sticky.check(); + expect($firstSticky.hasClass('material-sticky-active')).toBe(false); + }); + + it('pushes the active element when the next sticky element touches it', function() { + var firstTarget = { top: -10, bottom: 9, height: 19 }; + var firstActual = { top: 0, bottom: 19, height: 19 }; + var secondActual = { top: 18, bottom: 37, height: 19 }; + setup({ + containerScroll: 19, + firstActual: firstActual, + firstTarget: firstTarget, + secondActual: secondActual + }); + $firstSticky.attr('material-sticky-active', true); + $sticky.check(); + expect($firstSticky.data('translatedHeight')).toBe(-1); + }); + + it('increments the active element when it is pushed off screen', function() { + var firstActual = { top: -9, bottom: 0, height: 10 }; + setup({ + containerScroll: 10, + firstActual: firstActual + }); + $firstSticky.addClass('material-sticky-active'); + $sticky.check(); + expect($firstSticky.hasClass('material-sticky-active')).toBe(false); + expect($sticky.targetIndex).toBe(1); + }); + + it('pulls the previous element when the sticky element losens', function() { + var firstActual = { top: -10, bottom: -1, height: 9 }; + var firstTarget = { top: -50, bottom: -41, height: 9 }; + var secondActual = { top: 0, bottom: 9, height: 9 }; + setup({ + containerScroll: 30, + lastScroll: 31, + firstActual: firstActual, + firstTarget: firstTarget, + secondTarget: secondActual, + secondActual: secondActual + }); + $sticky.targetIndex = 0; + $firstSticky.data('translatedHeight', -10); + $firstSticky.addClass('material-sticky-active'); + $sticky.check(); + expect($firstSticky.data('translatedHeight')).toBe(-9); + }); +}); diff --git a/src/components/subheader/README.md b/src/components/subheader/README.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/components/subheader/_subheader.scss b/src/components/subheader/_subheader.scss new file mode 100644 index 00000000000..f6a0ca9d0b4 --- /dev/null +++ b/src/components/subheader/_subheader.scss @@ -0,0 +1,15 @@ +h2.material-subheader { + color: $text-medium; + display: block; + width: 100%; + font-size: $subheader-font-size; + font-weight: $subheader-font-weight; + line-height: $subheader-line-height; + padding: $subheader-padding; + margin: $subheader-margin; + background-color: white; + + &.material-subheader-colored { + color: $theme-light-blue + } +} diff --git a/src/components/subheader/demos/basic/index.html b/src/components/subheader/demos/basic/index.html new file mode 100644 index 00000000000..e69b7cbd039 --- /dev/null +++ b/src/components/subheader/demos/basic/index.html @@ -0,0 +1,62 @@ + +
+ +
+ Unread Messages + + + +
+ {{message.who}} +
+
+

{{message.what}}

+

{{message.who}}

+

+ {{message.notes}} +

+
+
+
+
+
+
+ Read Messages + + + +
+ {{message.who}} +
+
+

{{message.what}}

+

{{message.who}}

+

+ {{message.notes}} +

+
+
+
+
+
+
+ Archived messages + + + +
+ {{message.who}} +
+
+

{{message.what}}

+

{{message.who}}

+

+ {{message.notes}} +

+
+
+
+
+
+
+
diff --git a/src/components/subheader/demos/basic/script.js b/src/components/subheader/demos/basic/script.js new file mode 100644 index 00000000000..fb070063a8e --- /dev/null +++ b/src/components/subheader/demos/basic/script.js @@ -0,0 +1,84 @@ + +angular.module('app', ['ngMaterial']) + +.controller('SubheaderAppCtrl', function($scope) { + $scope.messages = [ + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + { + face : '/img/list/60.jpeg', + what: 'Brunch this weekend?', + who: 'Min Li Chan', + when: '3:08PM', + notes: " I'll be in your neighborhood doing errands" + }, + ]; +}); diff --git a/src/components/subheader/demos/basic/style.css b/src/components/subheader/demos/basic/style.css new file mode 100644 index 00000000000..342508b49c4 --- /dev/null +++ b/src/components/subheader/demos/basic/style.css @@ -0,0 +1,12 @@ + +material-content { + margin-right: 7px; +} + +.face { + border-radius: 30px; + border: 1px solid #ddd; + width: 48px; + margin: 16px; +} + diff --git a/src/components/subheader/module.json b/src/components/subheader/module.json new file mode 100644 index 00000000000..4523f77dc7a --- /dev/null +++ b/src/components/subheader/module.json @@ -0,0 +1,10 @@ +{ + "module": "material.components.subheader", + "name": "SubHeader", + "demos": { + "basic": { + "name": "Basic Usage", + "files": ["demos/basic/*"] + } + } +} diff --git a/src/components/subheader/subheader.js b/src/components/subheader/subheader.js new file mode 100644 index 00000000000..2e5b20205fb --- /dev/null +++ b/src/components/subheader/subheader.js @@ -0,0 +1,38 @@ +/** + * @ngdoc module + * @name material.components.subheader + * @description + * SubHeader module + */ +angular.module('material.components.subheader', [ + 'material.components.sticky' +]) +.directive('materialSubheader', [ + '$compile', + materialSubheaderDirective +]); + +/** + * @ngdoc directive + * @name materialSubheader + * @module material.components.subheader + * + * @restrict E + * + * @description + * The `` directive is a subheader for a section + * + * @usage + * + * Online Friends + * + */ + +function materialSubheaderDirective($compile) { + return { + restrict: 'E', + replace: true, + transclude: true, + template: '

' + }; +} diff --git a/src/components/subheader/subheader.spec.js b/src/components/subheader/subheader.spec.js new file mode 100644 index 00000000000..eb02b97fbbd --- /dev/null +++ b/src/components/subheader/subheader.spec.js @@ -0,0 +1,28 @@ +describe('materialSubheader', function() { + var $materialStickyMock, + basicHtml = 'Hello world!'; + + beforeEach(module('material.components.subheader', function($provide) { + $materialStickyMock = function() { + $materialStickyMock.args = Array.prototype.slice.call(arguments); + }; + $provide.value('$materialSticky', $materialStickyMock); + })); + + + it('should preserve content', inject(function($compile, $rootScope) { + var $scope = $rootScope.$new(); + $scope.to = 'world'; + var $el = $compile('
Hello {{ to }}!
')($scope); + $scope.$digest(); + var $subHeader = $el.children(); + expect($subHeader.text()).toEqual('Hello world!'); + })); + + it('should implement $materialSticky', inject(function($compile, $rootScope) { + var scope = $rootScope.$new(); + var $el = $compile(basicHtml)(scope); + expect($materialStickyMock.args[0]).toBe(scope); + })); + +}); diff --git a/src/components/toolbar/demo2/index.html b/src/components/toolbar/demo2/index.html index b422af58b16..1bf11478aaf 100644 --- a/src/components/toolbar/demo2/index.html +++ b/src/components/toolbar/demo2/index.html @@ -18,8 +18,8 @@

-

{{item.what}}

-

{{item.who}}

+

{{item.what}}

+

{{item.who}}

{{item.notes}}

diff --git a/src/core/style/theme/_variables.scss b/src/core/style/theme/_variables.scss index c2b93c2a052..20c48a54563 100644 --- a/src/core/style/theme/_variables.scss +++ b/src/core/style/theme/_variables.scss @@ -11,6 +11,7 @@ $background-color-base: #fff !default; // Font colors $text-light: #f1f1f1; +$text-medium: #999; $text-dark: #111111; // Theme colors @@ -113,17 +114,30 @@ $whiteframe-zindex-z4: 4; $whiteframe-shadow-z5: 0px 27px 24px 0 rgba(0,0,0,0.2); $whiteframe-zindex-z5: 5; +// SubHeader + +$subheader-line-height: 1em; +$subheader-font-size: 0.9em; +$subheader-padding: ($baseline-grid * 2) 0px ($baseline-grid * 2) ($baseline-grid * 2); +$subheader-font-weight: 400; +$subheader-margin: 0 0 0 0; + // Lists and tiles -$list-h2-font-size: 1.1em; -$list-h2-margin: 0 0 3px 0; -$list-h2-font-weight: 400; -$list-h3-font-size: 0.9em; -$list-h3-font-weight: 400; +$list-h3-font-size: 1.1em; $list-h3-margin: 0 0 3px 0; +$list-h3-font-weight: 400; +$list-h4-font-size: 0.9em; +$list-h4-font-weight: 400; +$list-h4-margin: 0 0 3px 0; $list-p-font-size: 0.75em; $list-p-margin: 0 0 3px 0; +$list-padding-top: $baseline-grid; +$list-padding-right: 0px; +$list-padding-left: 0px; +$list-padding-bottom: $baseline-grid; + $item-padding-top: 0px; $item-padding-right: 0px; $item-padding-left: 0px; diff --git a/src/core/util/util.js b/src/core/util/util.js index 57f99ee1d69..d05ad69aa6a 100644 --- a/src/core/util/util.js +++ b/src/core/util/util.js @@ -106,6 +106,23 @@ var Util = { }; }, + /** + * Wraps an element with a tag + * + * @param el element to wrap + * @param tag tag to wrap it with + * @param [className] optional class to apply to the wrapper + * @returns new element + * + */ + wrap: function(el, tag, className) { + if(el.hasOwnProperty(0)) { el = el[0]; } + var wrapper = document.createElement(tag); + wrapper.className += className; + wrapper.appendChild(el.parentNode.replaceChild(wrapper, el)); + return angular.element(wrapper); + }, + /** * nextUid, from angular.js. * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric diff --git a/src/main.scss b/src/main.scss index 6e4477e744c..82404f3ac93 100644 --- a/src/main.scss +++ b/src/main.scss @@ -22,6 +22,8 @@ "components/radioButton/radio-button", "components/sidenav/sidenav", "components/slider/slider", +"components/subheader/subheader", +"components/sticky/sticky", "components/switch/switch", "components/textField/textField", "components/toast/toast",