| /*! |
| * Angular Material Design |
| * https://github.com/angular/material |
| * @license MIT |
| * v1.0.1 |
| */ |
| (function( window, angular, undefined ){ |
| "use strict"; |
| |
| /** |
| * @ngdoc module |
| * @name material.components.gridList |
| */ |
| angular.module('material.components.gridList', ['material.core']) |
| .directive('mdGridList', GridListDirective) |
| .directive('mdGridTile', GridTileDirective) |
| .directive('mdGridTileFooter', GridTileCaptionDirective) |
| .directive('mdGridTileHeader', GridTileCaptionDirective) |
| .factory('$mdGridLayout', GridLayoutFactory); |
| |
| /** |
| * @ngdoc directive |
| * @name mdGridList |
| * @module material.components.gridList |
| * @restrict E |
| * @description |
| * Grid lists are an alternative to standard list views. Grid lists are distinct |
| * from grids used for layouts and other visual presentations. |
| * |
| * A grid list is best suited to presenting a homogenous data type, typically |
| * images, and is optimized for visual comprehension and differentiating between |
| * like data types. |
| * |
| * A grid list is a continuous element consisting of tessellated, regular |
| * subdivisions called cells that contain tiles (`md-grid-tile`). |
| * |
| * <img src="//material-design.storage.googleapis.com/publish/v_2/material_ext_publish/0Bx4BSt6jniD7OVlEaXZ5YmU1Xzg/components_grids_usage2.png" |
| * style="width: 300px; height: auto; margin-right: 16px;" alt="Concept of grid explained visually"> |
| * <img src="//material-design.storage.googleapis.com/publish/v_2/material_ext_publish/0Bx4BSt6jniD7VGhsOE5idWlJWXM/components_grids_usage3.png" |
| * style="width: 300px; height: auto;" alt="Grid concepts legend"> |
| * |
| * Cells are arrayed vertically and horizontally within the grid. |
| * |
| * Tiles hold content and can span one or more cells vertically or horizontally. |
| * |
| * ### Responsive Attributes |
| * |
| * The `md-grid-list` directive supports "responsive" attributes, which allow |
| * different `md-cols`, `md-gutter` and `md-row-height` values depending on the |
| * currently matching media query (as defined in `$mdConstant.MEDIA`). |
| * |
| * In order to set a responsive attribute, first define the fallback value with |
| * the standard attribute name, then add additional attributes with the |
| * following convention: `{base-attribute-name}-{media-query-name}="{value}"` |
| * (ie. `md-cols-lg="8"`) |
| * |
| * @param {number} md-cols Number of columns in the grid. |
| * @param {string} md-row-height One of |
| * <ul> |
| * <li>CSS length - Fixed height rows (eg. `8px` or `1rem`)</li> |
| * <li>`{width}:{height}` - Ratio of width to height (eg. |
| * `md-row-height="16:9"`)</li> |
| * <li>`"fit"` - Height will be determined by subdividing the available |
| * height by the number of rows</li> |
| * </ul> |
| * @param {string=} md-gutter The amount of space between tiles in CSS units |
| * (default 1px) |
| * @param {expression=} md-on-layout Expression to evaluate after layout. Event |
| * object is available as `$event`, and contains performance information. |
| * |
| * @usage |
| * Basic: |
| * <hljs lang="html"> |
| * <md-grid-list md-cols="5" md-gutter="1em" md-row-height="4:3"> |
| * <md-grid-tile></md-grid-tile> |
| * </md-grid-list> |
| * </hljs> |
| * |
| * Fixed-height rows: |
| * <hljs lang="html"> |
| * <md-grid-list md-cols="4" md-row-height="200px" ...> |
| * <md-grid-tile></md-grid-tile> |
| * </md-grid-list> |
| * </hljs> |
| * |
| * Fit rows: |
| * <hljs lang="html"> |
| * <md-grid-list md-cols="4" md-row-height="fit" style="height: 400px;" ...> |
| * <md-grid-tile></md-grid-tile> |
| * </md-grid-list> |
| * </hljs> |
| * |
| * Using responsive attributes: |
| * <hljs lang="html"> |
| * <md-grid-list |
| * md-cols-sm="2" |
| * md-cols-md="4" |
| * md-cols-lg="8" |
| * md-cols-gt-lg="12" |
| * ...> |
| * <md-grid-tile></md-grid-tile> |
| * </md-grid-list> |
| * </hljs> |
| */ |
| function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) { |
| return { |
| restrict: 'E', |
| controller: GridListController, |
| scope: { |
| mdOnLayout: '&' |
| }, |
| link: postLink |
| }; |
| |
| function postLink(scope, element, attrs, ctrl) { |
| // Apply semantics |
| element.attr('role', 'list'); |
| |
| // Provide the controller with a way to trigger layouts. |
| ctrl.layoutDelegate = layoutDelegate; |
| |
| var invalidateLayout = angular.bind(ctrl, ctrl.invalidateLayout), |
| unwatchAttrs = watchMedia(); |
| scope.$on('$destroy', unwatchMedia); |
| |
| /** |
| * Watches for changes in media, invalidating layout as necessary. |
| */ |
| function watchMedia() { |
| for (var mediaName in $mdConstant.MEDIA) { |
| $mdMedia(mediaName); // initialize |
| $mdMedia.getQuery($mdConstant.MEDIA[mediaName]) |
| .addListener(invalidateLayout); |
| } |
| return $mdMedia.watchResponsiveAttributes( |
| ['md-cols', 'md-row-height', 'md-gutter'], attrs, layoutIfMediaMatch); |
| } |
| |
| function unwatchMedia() { |
| ctrl.layoutDelegate = angular.noop; |
| |
| unwatchAttrs(); |
| for (var mediaName in $mdConstant.MEDIA) { |
| $mdMedia.getQuery($mdConstant.MEDIA[mediaName]) |
| .removeListener(invalidateLayout); |
| } |
| } |
| |
| /** |
| * Performs grid layout if the provided mediaName matches the currently |
| * active media type. |
| */ |
| function layoutIfMediaMatch(mediaName) { |
| if (mediaName == null) { |
| // TODO(shyndman): It would be nice to only layout if we have |
| // instances of attributes using this media type |
| ctrl.invalidateLayout(); |
| } else if ($mdMedia(mediaName)) { |
| ctrl.invalidateLayout(); |
| } |
| } |
| |
| var lastLayoutProps; |
| |
| /** |
| * Invokes the layout engine, and uses its results to lay out our |
| * tile elements. |
| * |
| * @param {boolean} tilesInvalidated Whether tiles have been |
| * added/removed/moved since the last layout. This is to avoid situations |
| * where tiles are replaced with properties identical to their removed |
| * counterparts. |
| */ |
| function layoutDelegate(tilesInvalidated) { |
| var tiles = getTileElements(); |
| var props = { |
| tileSpans: getTileSpans(tiles), |
| colCount: getColumnCount(), |
| rowMode: getRowMode(), |
| rowHeight: getRowHeight(), |
| gutter: getGutter() |
| }; |
| |
| if (!tilesInvalidated && angular.equals(props, lastLayoutProps)) { |
| return; |
| } |
| |
| var performance = |
| $mdGridLayout(props.colCount, props.tileSpans, tiles) |
| .map(function(tilePositions, rowCount) { |
| return { |
| grid: { |
| element: element, |
| style: getGridStyle(props.colCount, rowCount, |
| props.gutter, props.rowMode, props.rowHeight) |
| }, |
| tiles: tilePositions.map(function(ps, i) { |
| return { |
| element: angular.element(tiles[i]), |
| style: getTileStyle(ps.position, ps.spans, |
| props.colCount, rowCount, |
| props.gutter, props.rowMode, props.rowHeight) |
| } |
| }) |
| } |
| }) |
| .reflow() |
| .performance(); |
| |
| // Report layout |
| scope.mdOnLayout({ |
| $event: { |
| performance: performance |
| } |
| }); |
| |
| lastLayoutProps = props; |
| } |
| |
| // Use $interpolate to do some simple string interpolation as a convenience. |
| |
| var startSymbol = $interpolate.startSymbol(); |
| var endSymbol = $interpolate.endSymbol(); |
| |
| // Returns an expression wrapped in the interpolator's start and end symbols. |
| function expr(exprStr) { |
| return startSymbol + exprStr + endSymbol; |
| } |
| |
| // The amount of space a single 1x1 tile would take up (either width or height), used as |
| // a basis for other calculations. This consists of taking the base size percent (as would be |
| // if evenly dividing the size between cells), and then subtracting the size of one gutter. |
| // However, since there are no gutters on the edges, each tile only uses a fration |
| // (gutterShare = numGutters / numCells) of the gutter size. (Imagine having one gutter per |
| // tile, and then breaking up the extra gutter on the edge evenly among the cells). |
| var UNIT = $interpolate(expr('share') + '% - (' + expr('gutter') + ' * ' + expr('gutterShare') + ')'); |
| |
| // The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value. |
| // The position comes the size of a 1x1 tile plus gutter for each previous tile in the |
| // row/column (offset). |
| var POSITION = $interpolate('calc((' + expr('unit') + ' + ' + expr('gutter') + ') * ' + expr('offset') + ')'); |
| |
| // The actual size of a tile, e.g., width or height, taking rowSpan or colSpan into account. |
| // This is computed by multiplying the base unit by the rowSpan/colSpan, and then adding back |
| // in the space that the gutter would normally have used (which was already accounted for in |
| // the base unit calculation). |
| var DIMENSION = $interpolate('calc((' + expr('unit') + ') * ' + expr('span') + ' + (' + expr('span') + ' - 1) * ' + expr('gutter') + ')'); |
| |
| /** |
| * Gets the styles applied to a tile element described by the given parameters. |
| * @param {{row: number, col: number}} position The row and column indices of the tile. |
| * @param {{row: number, col: number}} spans The rowSpan and colSpan of the tile. |
| * @param {number} colCount The number of columns. |
| * @param {number} rowCount The number of rows. |
| * @param {string} gutter The amount of space between tiles. This will be something like |
| * '5px' or '2em'. |
| * @param {string} rowMode The row height mode. Can be one of: |
| * 'fixed': all rows have a fixed size, given by rowHeight, |
| * 'ratio': row height defined as a ratio to width, or |
| * 'fit': fit to the grid-list element height, divinding evenly among rows. |
| * @param {string|number} rowHeight The height of a row. This is only used for 'fixed' mode and |
| * for 'ratio' mode. For 'ratio' mode, this is the *ratio* of width-to-height (e.g., 0.75). |
| * @returns {Object} Map of CSS properties to be applied to the style element. Will define |
| * values for top, left, width, height, marginTop, and paddingTop. |
| */ |
| function getTileStyle(position, spans, colCount, rowCount, gutter, rowMode, rowHeight) { |
| // TODO(shyndman): There are style caching opportunities here. |
| |
| // Percent of the available horizontal space that one column takes up. |
| var hShare = (1 / colCount) * 100; |
| |
| // Fraction of the gutter size that each column takes up. |
| var hGutterShare = (colCount - 1) / colCount; |
| |
| // Base horizontal size of a column. |
| var hUnit = UNIT({share: hShare, gutterShare: hGutterShare, gutter: gutter}); |
| |
| // The width and horizontal position of each tile is always calculated the same way, but the |
| // height and vertical position depends on the rowMode. |
| var style = { |
| left: POSITION({ unit: hUnit, offset: position.col, gutter: gutter }), |
| width: DIMENSION({ unit: hUnit, span: spans.col, gutter: gutter }), |
| // resets |
| paddingTop: '', |
| marginTop: '', |
| top: '', |
| height: '' |
| }; |
| |
| switch (rowMode) { |
| case 'fixed': |
| // In fixed mode, simply use the given rowHeight. |
| style.top = POSITION({ unit: rowHeight, offset: position.row, gutter: gutter }); |
| style.height = DIMENSION({ unit: rowHeight, span: spans.row, gutter: gutter }); |
| break; |
| |
| case 'ratio': |
| // Percent of the available vertical space that one row takes up. Here, rowHeight holds |
| // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333. |
| var vShare = hShare / rowHeight; |
| |
| // Base veritcal size of a row. |
| var vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter }); |
| |
| // padidngTop and marginTop are used to maintain the given aspect ratio, as |
| // a percentage-based value for these properties is applied to the *width* of the |
| // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties |
| style.paddingTop = DIMENSION({ unit: vUnit, span: spans.row, gutter: gutter}); |
| style.marginTop = POSITION({ unit: vUnit, offset: position.row, gutter: gutter }); |
| break; |
| |
| case 'fit': |
| // Fraction of the gutter size that each column takes up. |
| var vGutterShare = (rowCount - 1) / rowCount; |
| |
| // Percent of the available vertical space that one row takes up. |
| var vShare = (1 / rowCount) * 100; |
| |
| // Base vertical size of a row. |
| var vUnit = UNIT({share: vShare, gutterShare: vGutterShare, gutter: gutter}); |
| |
| style.top = POSITION({unit: vUnit, offset: position.row, gutter: gutter}); |
| style.height = DIMENSION({unit: vUnit, span: spans.row, gutter: gutter}); |
| break; |
| } |
| |
| return style; |
| } |
| |
| function getGridStyle(colCount, rowCount, gutter, rowMode, rowHeight) { |
| var style = {}; |
| |
| switch(rowMode) { |
| case 'fixed': |
| style.height = DIMENSION({ unit: rowHeight, span: rowCount, gutter: gutter }); |
| style.paddingBottom = ''; |
| break; |
| |
| case 'ratio': |
| // rowHeight is width / height |
| var hGutterShare = colCount === 1 ? 0 : (colCount - 1) / colCount, |
| hShare = (1 / colCount) * 100, |
| vShare = hShare * (1 / rowHeight), |
| vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter }); |
| |
| style.height = ''; |
| style.paddingBottom = DIMENSION({ unit: vUnit, span: rowCount, gutter: gutter}); |
| break; |
| |
| case 'fit': |
| // noop, as the height is user set |
| break; |
| } |
| |
| return style; |
| } |
| |
| function getTileElements() { |
| return [].filter.call(element.children(), function(ele) { |
| return ele.tagName == 'MD-GRID-TILE' && !ele.$$mdDestroyed; |
| }); |
| } |
| |
| /** |
| * Gets an array of objects containing the rowspan and colspan for each tile. |
| * @returns {Array<{row: number, col: number}>} |
| */ |
| function getTileSpans(tileElements) { |
| return [].map.call(tileElements, function(ele) { |
| var ctrl = angular.element(ele).controller('mdGridTile'); |
| return { |
| row: parseInt( |
| $mdMedia.getResponsiveAttribute(ctrl.$attrs, 'md-rowspan'), 10) || 1, |
| col: parseInt( |
| $mdMedia.getResponsiveAttribute(ctrl.$attrs, 'md-colspan'), 10) || 1 |
| }; |
| }); |
| } |
| |
| function getColumnCount() { |
| var colCount = parseInt($mdMedia.getResponsiveAttribute(attrs, 'md-cols'), 10); |
| if (isNaN(colCount)) { |
| throw 'md-grid-list: md-cols attribute was not found, or contained a non-numeric value'; |
| } |
| return colCount; |
| } |
| |
| function getGutter() { |
| return applyDefaultUnit($mdMedia.getResponsiveAttribute(attrs, 'md-gutter') || 1); |
| } |
| |
| function getRowHeight() { |
| var rowHeight = $mdMedia.getResponsiveAttribute(attrs, 'md-row-height'); |
| switch (getRowMode()) { |
| case 'fixed': |
| return applyDefaultUnit(rowHeight); |
| case 'ratio': |
| var whRatio = rowHeight.split(':'); |
| return parseFloat(whRatio[0]) / parseFloat(whRatio[1]); |
| case 'fit': |
| return 0; // N/A |
| } |
| } |
| |
| function getRowMode() { |
| var rowHeight = $mdMedia.getResponsiveAttribute(attrs, 'md-row-height'); |
| if (rowHeight == 'fit') { |
| return 'fit'; |
| } else if (rowHeight.indexOf(':') !== -1) { |
| return 'ratio'; |
| } else { |
| return 'fixed'; |
| } |
| } |
| |
| function applyDefaultUnit(val) { |
| return /\D$/.test(val) ? val : val + 'px'; |
| } |
| } |
| } |
| GridListDirective.$inject = ["$interpolate", "$mdConstant", "$mdGridLayout", "$mdMedia"]; |
| |
| /* ngInject */ |
| function GridListController($mdUtil) { |
| this.layoutInvalidated = false; |
| this.tilesInvalidated = false; |
| this.$timeout_ = $mdUtil.nextTick; |
| this.layoutDelegate = angular.noop; |
| } |
| GridListController.$inject = ["$mdUtil"]; |
| |
| GridListController.prototype = { |
| invalidateTiles: function() { |
| this.tilesInvalidated = true; |
| this.invalidateLayout(); |
| }, |
| |
| invalidateLayout: function() { |
| if (this.layoutInvalidated) { |
| return; |
| } |
| this.layoutInvalidated = true; |
| this.$timeout_(angular.bind(this, this.layout)); |
| }, |
| |
| layout: function() { |
| try { |
| this.layoutDelegate(this.tilesInvalidated); |
| } finally { |
| this.layoutInvalidated = false; |
| this.tilesInvalidated = false; |
| } |
| } |
| }; |
| |
| |
| /* ngInject */ |
| function GridLayoutFactory($mdUtil) { |
| var defaultAnimator = GridTileAnimator; |
| |
| /** |
| * Set the reflow animator callback |
| */ |
| GridLayout.animateWith = function(customAnimator) { |
| defaultAnimator = !angular.isFunction(customAnimator) ? GridTileAnimator : customAnimator; |
| }; |
| |
| return GridLayout; |
| |
| /** |
| * Publish layout function |
| */ |
| function GridLayout(colCount, tileSpans) { |
| var self, layoutInfo, gridStyles, layoutTime, mapTime, reflowTime; |
| |
| layoutTime = $mdUtil.time(function() { |
| layoutInfo = calculateGridFor(colCount, tileSpans); |
| }); |
| |
| return self = { |
| |
| /** |
| * An array of objects describing each tile's position in the grid. |
| */ |
| layoutInfo: function() { |
| return layoutInfo; |
| }, |
| |
| /** |
| * Maps grid positioning to an element and a set of styles using the |
| * provided updateFn. |
| */ |
| map: function(updateFn) { |
| mapTime = $mdUtil.time(function() { |
| var info = self.layoutInfo(); |
| gridStyles = updateFn(info.positioning, info.rowCount); |
| }); |
| return self; |
| }, |
| |
| /** |
| * Default animator simply sets the element.css( <styles> ). An alternate |
| * animator can be provided as an argument. The function has the following |
| * signature: |
| * |
| * function({grid: {element: JQLite, style: Object}, tiles: Array<{element: JQLite, style: Object}>) |
| */ |
| reflow: function(animatorFn) { |
| reflowTime = $mdUtil.time(function() { |
| var animator = animatorFn || defaultAnimator; |
| animator(gridStyles.grid, gridStyles.tiles); |
| }); |
| return self; |
| }, |
| |
| /** |
| * Timing for the most recent layout run. |
| */ |
| performance: function() { |
| return { |
| tileCount: tileSpans.length, |
| layoutTime: layoutTime, |
| mapTime: mapTime, |
| reflowTime: reflowTime, |
| totalTime: layoutTime + mapTime + reflowTime |
| }; |
| } |
| }; |
| } |
| |
| /** |
| * Default Gridlist animator simple sets the css for each element; |
| * NOTE: any transitions effects must be manually set in the CSS. |
| * e.g. |
| * |
| * md-grid-tile { |
| * transition: all 700ms ease-out 50ms; |
| * } |
| * |
| */ |
| function GridTileAnimator(grid, tiles) { |
| grid.element.css(grid.style); |
| tiles.forEach(function(t) { |
| t.element.css(t.style); |
| }) |
| } |
| |
| /** |
| * Calculates the positions of tiles. |
| * |
| * The algorithm works as follows: |
| * An Array<Number> with length colCount (spaceTracker) keeps track of |
| * available tiling positions, where elements of value 0 represents an |
| * empty position. Space for a tile is reserved by finding a sequence of |
| * 0s with length <= than the tile's colspan. When such a space has been |
| * found, the occupied tile positions are incremented by the tile's |
| * rowspan value, as these positions have become unavailable for that |
| * many rows. |
| * |
| * If the end of a row has been reached without finding space for the |
| * tile, spaceTracker's elements are each decremented by 1 to a minimum |
| * of 0. Rows are searched in this fashion until space is found. |
| */ |
| function calculateGridFor(colCount, tileSpans) { |
| var curCol = 0, |
| curRow = 0, |
| spaceTracker = newSpaceTracker(); |
| |
| return { |
| positioning: tileSpans.map(function(spans, i) { |
| return { |
| spans: spans, |
| position: reserveSpace(spans, i) |
| }; |
| }), |
| rowCount: curRow + Math.max.apply(Math, spaceTracker) |
| }; |
| |
| function reserveSpace(spans, i) { |
| if (spans.col > colCount) { |
| throw 'md-grid-list: Tile at position ' + i + ' has a colspan ' + |
| '(' + spans.col + ') that exceeds the column count ' + |
| '(' + colCount + ')'; |
| } |
| |
| var start = 0, |
| end = 0; |
| |
| // TODO(shyndman): This loop isn't strictly necessary if you can |
| // determine the minimum number of rows before a space opens up. To do |
| // this, recognize that you've iterated across an entire row looking for |
| // space, and if so fast-forward by the minimum rowSpan count. Repeat |
| // until the required space opens up. |
| while (end - start < spans.col) { |
| if (curCol >= colCount) { |
| nextRow(); |
| continue; |
| } |
| |
| start = spaceTracker.indexOf(0, curCol); |
| if (start === -1 || (end = findEnd(start + 1)) === -1) { |
| start = end = 0; |
| nextRow(); |
| continue; |
| } |
| |
| curCol = end + 1; |
| } |
| |
| adjustRow(start, spans.col, spans.row); |
| curCol = start + spans.col; |
| |
| return { |
| col: start, |
| row: curRow |
| }; |
| } |
| |
| function nextRow() { |
| curCol = 0; |
| curRow++; |
| adjustRow(0, colCount, -1); // Decrement row spans by one |
| } |
| |
| function adjustRow(from, cols, by) { |
| for (var i = from; i < from + cols; i++) { |
| spaceTracker[i] = Math.max(spaceTracker[i] + by, 0); |
| } |
| } |
| |
| function findEnd(start) { |
| var i; |
| for (i = start; i < spaceTracker.length; i++) { |
| if (spaceTracker[i] !== 0) { |
| return i; |
| } |
| } |
| |
| if (i === spaceTracker.length) { |
| return i; |
| } |
| } |
| |
| function newSpaceTracker() { |
| var tracker = []; |
| for (var i = 0; i < colCount; i++) { |
| tracker.push(0); |
| } |
| return tracker; |
| } |
| } |
| } |
| GridLayoutFactory.$inject = ["$mdUtil"]; |
| |
| /** |
| * @ngdoc directive |
| * @name mdGridTile |
| * @module material.components.gridList |
| * @restrict E |
| * @description |
| * Tiles contain the content of an `md-grid-list`. They span one or more grid |
| * cells vertically or horizontally, and use `md-grid-tile-{footer,header}` to |
| * display secondary content. |
| * |
| * ### Responsive Attributes |
| * |
| * The `md-grid-tile` directive supports "responsive" attributes, which allow |
| * different `md-rowspan` and `md-colspan` values depending on the currently |
| * matching media query (as defined in `$mdConstant.MEDIA`). |
| * |
| * In order to set a responsive attribute, first define the fallback value with |
| * the standard attribute name, then add additional attributes with the |
| * following convention: `{base-attribute-name}-{media-query-name}="{value}"` |
| * (ie. `md-colspan-sm="4"`) |
| * |
| * @param {number=} md-colspan The number of columns to span (default 1). Cannot |
| * exceed the number of columns in the grid. Supports interpolation. |
| * @param {number=} md-rowspan The number of rows to span (default 1). Supports |
| * interpolation. |
| * |
| * @usage |
| * With header: |
| * <hljs lang="html"> |
| * <md-grid-tile> |
| * <md-grid-tile-header> |
| * <h3>This is a header</h3> |
| * </md-grid-tile-header> |
| * </md-grid-tile> |
| * </hljs> |
| * |
| * With footer: |
| * <hljs lang="html"> |
| * <md-grid-tile> |
| * <md-grid-tile-footer> |
| * <h3>This is a footer</h3> |
| * </md-grid-tile-footer> |
| * </md-grid-tile> |
| * </hljs> |
| * |
| * Spanning multiple rows/columns: |
| * <hljs lang="html"> |
| * <md-grid-tile md-colspan="2" md-rowspan="3"> |
| * </md-grid-tile> |
| * </hljs> |
| * |
| * Responsive attributes: |
| * <hljs lang="html"> |
| * <md-grid-tile md-colspan="1" md-colspan-sm="3" md-colspan-md="5"> |
| * </md-grid-tile> |
| * </hljs> |
| */ |
| function GridTileDirective($mdMedia) { |
| return { |
| restrict: 'E', |
| require: '^mdGridList', |
| template: '<figure ng-transclude></figure>', |
| transclude: true, |
| scope: {}, |
| // Simple controller that exposes attributes to the grid directive |
| controller: ["$attrs", function($attrs) { |
| this.$attrs = $attrs; |
| }], |
| link: postLink |
| }; |
| |
| function postLink(scope, element, attrs, gridCtrl) { |
| // Apply semantics |
| element.attr('role', 'listitem'); |
| |
| // If our colspan or rowspan changes, trigger a layout |
| var unwatchAttrs = $mdMedia.watchResponsiveAttributes(['md-colspan', 'md-rowspan'], |
| attrs, angular.bind(gridCtrl, gridCtrl.invalidateLayout)); |
| |
| // Tile registration/deregistration |
| gridCtrl.invalidateTiles(); |
| scope.$on('$destroy', function() { |
| // Mark the tile as destroyed so it is no longer considered in layout, |
| // even if the DOM element sticks around (like during a leave animation) |
| element[0].$$mdDestroyed = true; |
| unwatchAttrs(); |
| gridCtrl.invalidateLayout(); |
| }); |
| |
| if (angular.isDefined(scope.$parent.$index)) { |
| scope.$watch(function() { return scope.$parent.$index; }, |
| function indexChanged(newIdx, oldIdx) { |
| if (newIdx === oldIdx) { |
| return; |
| } |
| gridCtrl.invalidateTiles(); |
| }); |
| } |
| } |
| } |
| GridTileDirective.$inject = ["$mdMedia"]; |
| |
| |
| function GridTileCaptionDirective() { |
| return { |
| template: '<figcaption ng-transclude></figcaption>', |
| transclude: true |
| }; |
| } |
| |
| })(window, window.angular); |