UNPKG

38.6 kBJavaScriptView Raw
1import * as i0 from '@angular/core';
2import { InjectionToken, Component, ViewEncapsulation, ChangeDetectionStrategy, Optional, Inject, Input, ContentChildren, Directive, NgModule } from '@angular/core';
3import { setLines, MatLine, MatLineModule, MatCommonModule } from '@angular/material/core';
4import { coerceNumberProperty } from '@angular/cdk/coercion';
5import * as i1 from '@angular/cdk/bidi';
6
7/**
8 * @license
9 * Copyright Google LLC All Rights Reserved.
10 *
11 * Use of this source code is governed by an MIT-style license that can be
12 * found in the LICENSE file at https://angular.io/license
13 */
14/**
15 * Class for determining, from a list of tiles, the (row, col) position of each of those tiles
16 * in the grid. This is necessary (rather than just rendering the tiles in normal document flow)
17 * because the tiles can have a rowspan.
18 *
19 * The positioning algorithm greedily places each tile as soon as it encounters a gap in the grid
20 * large enough to accommodate it so that the tiles still render in the same order in which they
21 * are given.
22 *
23 * The basis of the algorithm is the use of an array to track the already placed tiles. Each
24 * element of the array corresponds to a column, and the value indicates how many cells in that
25 * column are already occupied; zero indicates an empty cell. Moving "down" to the next row
26 * decrements each value in the tracking array (indicating that the column is one cell closer to
27 * being free).
28 *
29 * @docs-private
30 */
31class TileCoordinator {
32 constructor() {
33 /** Index at which the search for the next gap will start. */
34 this.columnIndex = 0;
35 /** The current row index. */
36 this.rowIndex = 0;
37 }
38 /** Gets the total number of rows occupied by tiles */
39 get rowCount() {
40 return this.rowIndex + 1;
41 }
42 /**
43 * Gets the total span of rows occupied by tiles.
44 * Ex: A list with 1 row that contains a tile with rowspan 2 will have a total rowspan of 2.
45 */
46 get rowspan() {
47 const lastRowMax = Math.max(...this.tracker);
48 // if any of the tiles has a rowspan that pushes it beyond the total row count,
49 // add the difference to the rowcount
50 return lastRowMax > 1 ? this.rowCount + lastRowMax - 1 : this.rowCount;
51 }
52 /**
53 * Updates the tile positions.
54 * @param numColumns Amount of columns in the grid.
55 * @param tiles Tiles to be positioned.
56 */
57 update(numColumns, tiles) {
58 this.columnIndex = 0;
59 this.rowIndex = 0;
60 this.tracker = new Array(numColumns);
61 this.tracker.fill(0, 0, this.tracker.length);
62 this.positions = tiles.map(tile => this._trackTile(tile));
63 }
64 /** Calculates the row and col position of a tile. */
65 _trackTile(tile) {
66 // Find a gap large enough for this tile.
67 const gapStartIndex = this._findMatchingGap(tile.colspan);
68 // Place tile in the resulting gap.
69 this._markTilePosition(gapStartIndex, tile);
70 // The next time we look for a gap, the search will start at columnIndex, which should be
71 // immediately after the tile that has just been placed.
72 this.columnIndex = gapStartIndex + tile.colspan;
73 return new TilePosition(this.rowIndex, gapStartIndex);
74 }
75 /** Finds the next available space large enough to fit the tile. */
76 _findMatchingGap(tileCols) {
77 if (tileCols > this.tracker.length && (typeof ngDevMode === 'undefined' || ngDevMode)) {
78 throw Error(`mat-grid-list: tile with colspan ${tileCols} is wider than ` +
79 `grid with cols="${this.tracker.length}".`);
80 }
81 // Start index is inclusive, end index is exclusive.
82 let gapStartIndex = -1;
83 let gapEndIndex = -1;
84 // Look for a gap large enough to fit the given tile. Empty spaces are marked with a zero.
85 do {
86 // If we've reached the end of the row, go to the next row.
87 if (this.columnIndex + tileCols > this.tracker.length) {
88 this._nextRow();
89 gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
90 gapEndIndex = this._findGapEndIndex(gapStartIndex);
91 continue;
92 }
93 gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
94 // If there are no more empty spaces in this row at all, move on to the next row.
95 if (gapStartIndex == -1) {
96 this._nextRow();
97 gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
98 gapEndIndex = this._findGapEndIndex(gapStartIndex);
99 continue;
100 }
101 gapEndIndex = this._findGapEndIndex(gapStartIndex);
102 // If a gap large enough isn't found, we want to start looking immediately after the current
103 // gap on the next iteration.
104 this.columnIndex = gapStartIndex + 1;
105 // Continue iterating until we find a gap wide enough for this tile. Since gapEndIndex is
106 // exclusive, gapEndIndex is 0 means we didn't find a gap and should continue.
107 } while (gapEndIndex - gapStartIndex < tileCols || gapEndIndex == 0);
108 // If we still didn't manage to find a gap, ensure that the index is
109 // at least zero so the tile doesn't get pulled out of the grid.
110 return Math.max(gapStartIndex, 0);
111 }
112 /** Move "down" to the next row. */
113 _nextRow() {
114 this.columnIndex = 0;
115 this.rowIndex++;
116 // Decrement all spaces by one to reflect moving down one row.
117 for (let i = 0; i < this.tracker.length; i++) {
118 this.tracker[i] = Math.max(0, this.tracker[i] - 1);
119 }
120 }
121 /**
122 * Finds the end index (exclusive) of a gap given the index from which to start looking.
123 * The gap ends when a non-zero value is found.
124 */
125 _findGapEndIndex(gapStartIndex) {
126 for (let i = gapStartIndex + 1; i < this.tracker.length; i++) {
127 if (this.tracker[i] != 0) {
128 return i;
129 }
130 }
131 // The gap ends with the end of the row.
132 return this.tracker.length;
133 }
134 /** Update the tile tracker to account for the given tile in the given space. */
135 _markTilePosition(start, tile) {
136 for (let i = 0; i < tile.colspan; i++) {
137 this.tracker[start + i] = tile.rowspan;
138 }
139 }
140}
141/**
142 * Simple data structure for tile position (row, col).
143 * @docs-private
144 */
145class TilePosition {
146 constructor(row, col) {
147 this.row = row;
148 this.col = col;
149 }
150}
151
152/**
153 * @license
154 * Copyright Google LLC All Rights Reserved.
155 *
156 * Use of this source code is governed by an MIT-style license that can be
157 * found in the LICENSE file at https://angular.io/license
158 */
159/**
160 * Injection token used to provide a grid list to a tile and to avoid circular imports.
161 * @docs-private
162 */
163const MAT_GRID_LIST = new InjectionToken('MAT_GRID_LIST');
164
165/**
166 * @license
167 * Copyright Google LLC All Rights Reserved.
168 *
169 * Use of this source code is governed by an MIT-style license that can be
170 * found in the LICENSE file at https://angular.io/license
171 */
172class MatGridTile {
173 constructor(_element, _gridList) {
174 this._element = _element;
175 this._gridList = _gridList;
176 this._rowspan = 1;
177 this._colspan = 1;
178 }
179 /** Amount of rows that the grid tile takes up. */
180 get rowspan() {
181 return this._rowspan;
182 }
183 set rowspan(value) {
184 this._rowspan = Math.round(coerceNumberProperty(value));
185 }
186 /** Amount of columns that the grid tile takes up. */
187 get colspan() {
188 return this._colspan;
189 }
190 set colspan(value) {
191 this._colspan = Math.round(coerceNumberProperty(value));
192 }
193 /**
194 * Sets the style of the grid-tile element. Needs to be set manually to avoid
195 * "Changed after checked" errors that would occur with HostBinding.
196 */
197 _setStyle(property, value) {
198 this._element.nativeElement.style[property] = value;
199 }
200}
201MatGridTile.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTile, deps: [{ token: i0.ElementRef }, { token: MAT_GRID_LIST, optional: true }], target: i0.ɵɵFactoryTarget.Component });
202MatGridTile.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatGridTile, selector: "mat-grid-tile", inputs: { rowspan: "rowspan", colspan: "colspan" }, host: { properties: { "attr.rowspan": "rowspan", "attr.colspan": "colspan" }, classAttribute: "mat-grid-tile" }, exportAs: ["matGridTile"], ngImport: i0, template: "<div class=\"mat-grid-tile-content\">\n <ng-content></ng-content>\n</div>\n", styles: [".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
203i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTile, decorators: [{
204 type: Component,
205 args: [{ selector: 'mat-grid-tile', exportAs: 'matGridTile', host: {
206 'class': 'mat-grid-tile',
207 // Ensures that the "rowspan" and "colspan" input value is reflected in
208 // the DOM. This is needed for the grid-tile harness.
209 '[attr.rowspan]': 'rowspan',
210 '[attr.colspan]': 'colspan',
211 }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"mat-grid-tile-content\">\n <ng-content></ng-content>\n</div>\n", styles: [".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}"] }]
212 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: undefined, decorators: [{
213 type: Optional
214 }, {
215 type: Inject,
216 args: [MAT_GRID_LIST]
217 }] }]; }, propDecorators: { rowspan: [{
218 type: Input
219 }], colspan: [{
220 type: Input
221 }] } });
222class MatGridTileText {
223 constructor(_element) {
224 this._element = _element;
225 }
226 ngAfterContentInit() {
227 setLines(this._lines, this._element);
228 }
229}
230MatGridTileText.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileText, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
231MatGridTileText.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatGridTileText, selector: "mat-grid-tile-header, mat-grid-tile-footer", queries: [{ propertyName: "_lines", predicate: MatLine, descendants: true }], ngImport: i0, template: "<ng-content select=\"[mat-grid-avatar], [matGridAvatar]\"></ng-content>\n<div class=\"mat-grid-list-text\"><ng-content select=\"[mat-line], [matLine]\"></ng-content></div>\n<ng-content></ng-content>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
232i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileText, decorators: [{
233 type: Component,
234 args: [{ selector: 'mat-grid-tile-header, mat-grid-tile-footer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<ng-content select=\"[mat-grid-avatar], [matGridAvatar]\"></ng-content>\n<div class=\"mat-grid-list-text\"><ng-content select=\"[mat-line], [matLine]\"></ng-content></div>\n<ng-content></ng-content>\n" }]
235 }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { _lines: [{
236 type: ContentChildren,
237 args: [MatLine, { descendants: true }]
238 }] } });
239/**
240 * Directive whose purpose is to add the mat- CSS styling to this selector.
241 * @docs-private
242 */
243class MatGridAvatarCssMatStyler {
244}
245MatGridAvatarCssMatStyler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridAvatarCssMatStyler, deps: [], target: i0.ɵɵFactoryTarget.Directive });
246MatGridAvatarCssMatStyler.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatGridAvatarCssMatStyler, selector: "[mat-grid-avatar], [matGridAvatar]", host: { classAttribute: "mat-grid-avatar" }, ngImport: i0 });
247i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridAvatarCssMatStyler, decorators: [{
248 type: Directive,
249 args: [{
250 selector: '[mat-grid-avatar], [matGridAvatar]',
251 host: { 'class': 'mat-grid-avatar' },
252 }]
253 }] });
254/**
255 * Directive whose purpose is to add the mat- CSS styling to this selector.
256 * @docs-private
257 */
258class MatGridTileHeaderCssMatStyler {
259}
260MatGridTileHeaderCssMatStyler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileHeaderCssMatStyler, deps: [], target: i0.ɵɵFactoryTarget.Directive });
261MatGridTileHeaderCssMatStyler.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatGridTileHeaderCssMatStyler, selector: "mat-grid-tile-header", host: { classAttribute: "mat-grid-tile-header" }, ngImport: i0 });
262i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileHeaderCssMatStyler, decorators: [{
263 type: Directive,
264 args: [{
265 selector: 'mat-grid-tile-header',
266 host: { 'class': 'mat-grid-tile-header' },
267 }]
268 }] });
269/**
270 * Directive whose purpose is to add the mat- CSS styling to this selector.
271 * @docs-private
272 */
273class MatGridTileFooterCssMatStyler {
274}
275MatGridTileFooterCssMatStyler.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileFooterCssMatStyler, deps: [], target: i0.ɵɵFactoryTarget.Directive });
276MatGridTileFooterCssMatStyler.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: MatGridTileFooterCssMatStyler, selector: "mat-grid-tile-footer", host: { classAttribute: "mat-grid-tile-footer" }, ngImport: i0 });
277i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridTileFooterCssMatStyler, decorators: [{
278 type: Directive,
279 args: [{
280 selector: 'mat-grid-tile-footer',
281 host: { 'class': 'mat-grid-tile-footer' },
282 }]
283 }] });
284
285/**
286 * @license
287 * Copyright Google LLC All Rights Reserved.
288 *
289 * Use of this source code is governed by an MIT-style license that can be
290 * found in the LICENSE file at https://angular.io/license
291 */
292/**
293 * RegExp that can be used to check whether a value will
294 * be allowed inside a CSS `calc()` expression.
295 */
296const cssCalcAllowedValue = /^-?\d+((\.\d+)?[A-Za-z%$]?)+$/;
297/**
298 * Sets the style properties for an individual tile, given the position calculated by the
299 * Tile Coordinator.
300 * @docs-private
301 */
302class TileStyler {
303 constructor() {
304 this._rows = 0;
305 this._rowspan = 0;
306 }
307 /**
308 * Adds grid-list layout info once it is available. Cannot be processed in the constructor
309 * because these properties haven't been calculated by that point.
310 *
311 * @param gutterSize Size of the grid's gutter.
312 * @param tracker Instance of the TileCoordinator.
313 * @param cols Amount of columns in the grid.
314 * @param direction Layout direction of the grid.
315 */
316 init(gutterSize, tracker, cols, direction) {
317 this._gutterSize = normalizeUnits(gutterSize);
318 this._rows = tracker.rowCount;
319 this._rowspan = tracker.rowspan;
320 this._cols = cols;
321 this._direction = direction;
322 }
323 /**
324 * Computes the amount of space a single 1x1 tile would take up (width or height).
325 * Used as a basis for other calculations.
326 * @param sizePercent Percent of the total grid-list space that one 1x1 tile would take up.
327 * @param gutterFraction Fraction of the gutter size taken up by one 1x1 tile.
328 * @return The size of a 1x1 tile as an expression that can be evaluated via CSS calc().
329 */
330 getBaseTileSize(sizePercent, gutterFraction) {
331 // Take the base size percent (as would be if evenly dividing the size between cells),
332 // and then subtracting the size of one gutter. However, since there are no gutters on the
333 // edges, each tile only uses a fraction (gutterShare = numGutters / numCells) of the gutter
334 // size. (Imagine having one gutter per tile, and then breaking up the extra gutter on the
335 // edge evenly among the cells).
336 return `(${sizePercent}% - (${this._gutterSize} * ${gutterFraction}))`;
337 }
338 /**
339 * Gets The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value.
340 * @param offset Number of tiles that have already been rendered in the row/column.
341 * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).
342 * @return Position of the tile as a CSS calc() expression.
343 */
344 getTilePosition(baseSize, offset) {
345 // The position comes the size of a 1x1 tile plus gutter for each previous tile in the
346 // row/column (offset).
347 return offset === 0 ? '0' : calc(`(${baseSize} + ${this._gutterSize}) * ${offset}`);
348 }
349 /**
350 * Gets the actual size of a tile, e.g., width or height, taking rowspan or colspan into account.
351 * @param baseSize Base size of a 1x1 tile (as computed in getBaseTileSize).
352 * @param span The tile's rowspan or colspan.
353 * @return Size of the tile as a CSS calc() expression.
354 */
355 getTileSize(baseSize, span) {
356 return `(${baseSize} * ${span}) + (${span - 1} * ${this._gutterSize})`;
357 }
358 /**
359 * Sets the style properties to be applied to a tile for the given row and column index.
360 * @param tile Tile to which to apply the styling.
361 * @param rowIndex Index of the tile's row.
362 * @param colIndex Index of the tile's column.
363 */
364 setStyle(tile, rowIndex, colIndex) {
365 // Percent of the available horizontal space that one column takes up.
366 let percentWidthPerTile = 100 / this._cols;
367 // Fraction of the vertical gutter size that each column takes up.
368 // For example, if there are 5 columns, each column uses 4/5 = 0.8 times the gutter width.
369 let gutterWidthFractionPerTile = (this._cols - 1) / this._cols;
370 this.setColStyles(tile, colIndex, percentWidthPerTile, gutterWidthFractionPerTile);
371 this.setRowStyles(tile, rowIndex, percentWidthPerTile, gutterWidthFractionPerTile);
372 }
373 /** Sets the horizontal placement of the tile in the list. */
374 setColStyles(tile, colIndex, percentWidth, gutterWidth) {
375 // Base horizontal size of a column.
376 let baseTileWidth = this.getBaseTileSize(percentWidth, gutterWidth);
377 // The width and horizontal position of each tile is always calculated the same way, but the
378 // height and vertical position depends on the rowMode.
379 let side = this._direction === 'rtl' ? 'right' : 'left';
380 tile._setStyle(side, this.getTilePosition(baseTileWidth, colIndex));
381 tile._setStyle('width', calc(this.getTileSize(baseTileWidth, tile.colspan)));
382 }
383 /**
384 * Calculates the total size taken up by gutters across one axis of a list.
385 */
386 getGutterSpan() {
387 return `${this._gutterSize} * (${this._rowspan} - 1)`;
388 }
389 /**
390 * Calculates the total size taken up by tiles across one axis of a list.
391 * @param tileHeight Height of the tile.
392 */
393 getTileSpan(tileHeight) {
394 return `${this._rowspan} * ${this.getTileSize(tileHeight, 1)}`;
395 }
396 /**
397 * Calculates the computed height and returns the correct style property to set.
398 * This method can be implemented by each type of TileStyler.
399 * @docs-private
400 */
401 getComputedHeight() {
402 return null;
403 }
404}
405/**
406 * This type of styler is instantiated when the user passes in a fixed row height.
407 * Example `<mat-grid-list cols="3" rowHeight="100px">`
408 * @docs-private
409 */
410class FixedTileStyler extends TileStyler {
411 constructor(fixedRowHeight) {
412 super();
413 this.fixedRowHeight = fixedRowHeight;
414 }
415 init(gutterSize, tracker, cols, direction) {
416 super.init(gutterSize, tracker, cols, direction);
417 this.fixedRowHeight = normalizeUnits(this.fixedRowHeight);
418 if (!cssCalcAllowedValue.test(this.fixedRowHeight) &&
419 (typeof ngDevMode === 'undefined' || ngDevMode)) {
420 throw Error(`Invalid value "${this.fixedRowHeight}" set as rowHeight.`);
421 }
422 }
423 setRowStyles(tile, rowIndex) {
424 tile._setStyle('top', this.getTilePosition(this.fixedRowHeight, rowIndex));
425 tile._setStyle('height', calc(this.getTileSize(this.fixedRowHeight, tile.rowspan)));
426 }
427 getComputedHeight() {
428 return ['height', calc(`${this.getTileSpan(this.fixedRowHeight)} + ${this.getGutterSpan()}`)];
429 }
430 reset(list) {
431 list._setListStyle(['height', null]);
432 if (list._tiles) {
433 list._tiles.forEach(tile => {
434 tile._setStyle('top', null);
435 tile._setStyle('height', null);
436 });
437 }
438 }
439}
440/**
441 * This type of styler is instantiated when the user passes in a width:height ratio
442 * for the row height. Example `<mat-grid-list cols="3" rowHeight="3:1">`
443 * @docs-private
444 */
445class RatioTileStyler extends TileStyler {
446 constructor(value) {
447 super();
448 this._parseRatio(value);
449 }
450 setRowStyles(tile, rowIndex, percentWidth, gutterWidth) {
451 let percentHeightPerTile = percentWidth / this.rowHeightRatio;
452 this.baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterWidth);
453 // Use padding-top and margin-top to maintain the given aspect ratio, as
454 // a percentage-based value for these properties is applied versus the *width* of the
455 // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties
456 tile._setStyle('marginTop', this.getTilePosition(this.baseTileHeight, rowIndex));
457 tile._setStyle('paddingTop', calc(this.getTileSize(this.baseTileHeight, tile.rowspan)));
458 }
459 getComputedHeight() {
460 return [
461 'paddingBottom',
462 calc(`${this.getTileSpan(this.baseTileHeight)} + ${this.getGutterSpan()}`),
463 ];
464 }
465 reset(list) {
466 list._setListStyle(['paddingBottom', null]);
467 list._tiles.forEach(tile => {
468 tile._setStyle('marginTop', null);
469 tile._setStyle('paddingTop', null);
470 });
471 }
472 _parseRatio(value) {
473 const ratioParts = value.split(':');
474 if (ratioParts.length !== 2 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
475 throw Error(`mat-grid-list: invalid ratio given for row-height: "${value}"`);
476 }
477 this.rowHeightRatio = parseFloat(ratioParts[0]) / parseFloat(ratioParts[1]);
478 }
479}
480/**
481 * This type of styler is instantiated when the user selects a "fit" row height mode.
482 * In other words, the row height will reflect the total height of the container divided
483 * by the number of rows. Example `<mat-grid-list cols="3" rowHeight="fit">`
484 *
485 * @docs-private
486 */
487class FitTileStyler extends TileStyler {
488 setRowStyles(tile, rowIndex) {
489 // Percent of the available vertical space that one row takes up.
490 let percentHeightPerTile = 100 / this._rowspan;
491 // Fraction of the horizontal gutter size that each column takes up.
492 let gutterHeightPerTile = (this._rows - 1) / this._rows;
493 // Base vertical size of a column.
494 let baseTileHeight = this.getBaseTileSize(percentHeightPerTile, gutterHeightPerTile);
495 tile._setStyle('top', this.getTilePosition(baseTileHeight, rowIndex));
496 tile._setStyle('height', calc(this.getTileSize(baseTileHeight, tile.rowspan)));
497 }
498 reset(list) {
499 if (list._tiles) {
500 list._tiles.forEach(tile => {
501 tile._setStyle('top', null);
502 tile._setStyle('height', null);
503 });
504 }
505 }
506}
507/** Wraps a CSS string in a calc function */
508function calc(exp) {
509 return `calc(${exp})`;
510}
511/** Appends pixels to a CSS string if no units are given. */
512function normalizeUnits(value) {
513 return value.match(/([A-Za-z%]+)$/) ? value : `${value}px`;
514}
515
516/**
517 * @license
518 * Copyright Google LLC All Rights Reserved.
519 *
520 * Use of this source code is governed by an MIT-style license that can be
521 * found in the LICENSE file at https://angular.io/license
522 */
523// TODO(kara): Conditional (responsive) column count / row size.
524// TODO(kara): Re-layout on window resize / media change (debounced).
525// TODO(kara): gridTileHeader and gridTileFooter.
526const MAT_FIT_MODE = 'fit';
527class MatGridList {
528 constructor(_element, _dir) {
529 this._element = _element;
530 this._dir = _dir;
531 /** The amount of space between tiles. This will be something like '5px' or '2em'. */
532 this._gutter = '1px';
533 }
534 /** Amount of columns in the grid list. */
535 get cols() {
536 return this._cols;
537 }
538 set cols(value) {
539 this._cols = Math.max(1, Math.round(coerceNumberProperty(value)));
540 }
541 /** Size of the grid list's gutter in pixels. */
542 get gutterSize() {
543 return this._gutter;
544 }
545 set gutterSize(value) {
546 this._gutter = `${value == null ? '' : value}`;
547 }
548 /** Set internal representation of row height from the user-provided value. */
549 get rowHeight() {
550 return this._rowHeight;
551 }
552 set rowHeight(value) {
553 const newValue = `${value == null ? '' : value}`;
554 if (newValue !== this._rowHeight) {
555 this._rowHeight = newValue;
556 this._setTileStyler(this._rowHeight);
557 }
558 }
559 ngOnInit() {
560 this._checkCols();
561 this._checkRowHeight();
562 }
563 /**
564 * The layout calculation is fairly cheap if nothing changes, so there's little cost
565 * to run it frequently.
566 */
567 ngAfterContentChecked() {
568 this._layoutTiles();
569 }
570 /** Throw a friendly error if cols property is missing */
571 _checkCols() {
572 if (!this.cols && (typeof ngDevMode === 'undefined' || ngDevMode)) {
573 throw Error(`mat-grid-list: must pass in number of columns. ` + `Example: <mat-grid-list cols="3">`);
574 }
575 }
576 /** Default to equal width:height if rowHeight property is missing */
577 _checkRowHeight() {
578 if (!this._rowHeight) {
579 this._setTileStyler('1:1');
580 }
581 }
582 /** Creates correct Tile Styler subtype based on rowHeight passed in by user */
583 _setTileStyler(rowHeight) {
584 if (this._tileStyler) {
585 this._tileStyler.reset(this);
586 }
587 if (rowHeight === MAT_FIT_MODE) {
588 this._tileStyler = new FitTileStyler();
589 }
590 else if (rowHeight && rowHeight.indexOf(':') > -1) {
591 this._tileStyler = new RatioTileStyler(rowHeight);
592 }
593 else {
594 this._tileStyler = new FixedTileStyler(rowHeight);
595 }
596 }
597 /** Computes and applies the size and position for all children grid tiles. */
598 _layoutTiles() {
599 if (!this._tileCoordinator) {
600 this._tileCoordinator = new TileCoordinator();
601 }
602 const tracker = this._tileCoordinator;
603 const tiles = this._tiles.filter(tile => !tile._gridList || tile._gridList === this);
604 const direction = this._dir ? this._dir.value : 'ltr';
605 this._tileCoordinator.update(this.cols, tiles);
606 this._tileStyler.init(this.gutterSize, tracker, this.cols, direction);
607 tiles.forEach((tile, index) => {
608 const pos = tracker.positions[index];
609 this._tileStyler.setStyle(tile, pos.row, pos.col);
610 });
611 this._setListStyle(this._tileStyler.getComputedHeight());
612 }
613 /** Sets style on the main grid-list element, given the style name and value. */
614 _setListStyle(style) {
615 if (style) {
616 this._element.nativeElement.style[style[0]] = style[1];
617 }
618 }
619}
620MatGridList.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridList, deps: [{ token: i0.ElementRef }, { token: i1.Directionality, optional: true }], target: i0.ɵɵFactoryTarget.Component });
621MatGridList.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: MatGridList, selector: "mat-grid-list", inputs: { cols: "cols", gutterSize: "gutterSize", rowHeight: "rowHeight" }, host: { properties: { "attr.cols": "cols" }, classAttribute: "mat-grid-list" }, providers: [
622 {
623 provide: MAT_GRID_LIST,
624 useExisting: MatGridList,
625 },
626 ], queries: [{ propertyName: "_tiles", predicate: MatGridTile, descendants: true }], exportAs: ["matGridList"], ngImport: i0, template: "<div>\n <ng-content></ng-content>\n</div>", styles: [".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
627i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridList, decorators: [{
628 type: Component,
629 args: [{ selector: 'mat-grid-list', exportAs: 'matGridList', host: {
630 'class': 'mat-grid-list',
631 // Ensures that the "cols" input value is reflected in the DOM. This is
632 // needed for the grid-list harness.
633 '[attr.cols]': 'cols',
634 }, providers: [
635 {
636 provide: MAT_GRID_LIST,
637 useExisting: MatGridList,
638 },
639 ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div>\n <ng-content></ng-content>\n</div>", styles: [".mat-grid-list{display:block;position:relative}.mat-grid-tile{display:block;position:absolute;overflow:hidden}.mat-grid-tile .mat-grid-tile-header,.mat-grid-tile .mat-grid-tile-footer{display:flex;align-items:center;height:48px;color:#fff;background:rgba(0,0,0,.38);overflow:hidden;padding:0 16px;position:absolute;left:0;right:0}.mat-grid-tile .mat-grid-tile-header>*,.mat-grid-tile .mat-grid-tile-footer>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-tile-header.mat-2-line,.mat-grid-tile .mat-grid-tile-footer.mat-2-line{height:68px}.mat-grid-tile .mat-grid-list-text{display:flex;flex-direction:column;flex:auto;box-sizing:border-box;overflow:hidden}.mat-grid-tile .mat-grid-list-text>*{margin:0;padding:0;font-weight:normal;font-size:inherit}.mat-grid-tile .mat-grid-list-text:empty{display:none}.mat-grid-tile .mat-grid-tile-header{top:0}.mat-grid-tile .mat-grid-tile-footer{bottom:0}.mat-grid-tile .mat-grid-avatar{padding-right:16px}[dir=rtl] .mat-grid-tile .mat-grid-avatar{padding-right:0;padding-left:16px}.mat-grid-tile .mat-grid-avatar:empty{display:none}.mat-grid-tile-content{top:0;left:0;right:0;bottom:0;position:absolute;display:flex;align-items:center;justify-content:center;height:100%;padding:0;margin:0}"] }]
640 }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.Directionality, decorators: [{
641 type: Optional
642 }] }]; }, propDecorators: { _tiles: [{
643 type: ContentChildren,
644 args: [MatGridTile, { descendants: true }]
645 }], cols: [{
646 type: Input
647 }], gutterSize: [{
648 type: Input
649 }], rowHeight: [{
650 type: Input
651 }] } });
652
653/**
654 * @license
655 * Copyright Google LLC All Rights Reserved.
656 *
657 * Use of this source code is governed by an MIT-style license that can be
658 * found in the LICENSE file at https://angular.io/license
659 */
660class MatGridListModule {
661}
662MatGridListModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridListModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
663MatGridListModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: MatGridListModule, declarations: [MatGridList,
664 MatGridTile,
665 MatGridTileText,
666 MatGridTileHeaderCssMatStyler,
667 MatGridTileFooterCssMatStyler,
668 MatGridAvatarCssMatStyler], imports: [MatLineModule, MatCommonModule], exports: [MatGridList,
669 MatGridTile,
670 MatGridTileText,
671 MatLineModule,
672 MatCommonModule,
673 MatGridTileHeaderCssMatStyler,
674 MatGridTileFooterCssMatStyler,
675 MatGridAvatarCssMatStyler] });
676MatGridListModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridListModule, imports: [MatLineModule, MatCommonModule, MatLineModule,
677 MatCommonModule] });
678i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: MatGridListModule, decorators: [{
679 type: NgModule,
680 args: [{
681 imports: [MatLineModule, MatCommonModule],
682 exports: [
683 MatGridList,
684 MatGridTile,
685 MatGridTileText,
686 MatLineModule,
687 MatCommonModule,
688 MatGridTileHeaderCssMatStyler,
689 MatGridTileFooterCssMatStyler,
690 MatGridAvatarCssMatStyler,
691 ],
692 declarations: [
693 MatGridList,
694 MatGridTile,
695 MatGridTileText,
696 MatGridTileHeaderCssMatStyler,
697 MatGridTileFooterCssMatStyler,
698 MatGridAvatarCssMatStyler,
699 ],
700 }]
701 }] });
702
703/**
704 * @license
705 * Copyright Google LLC All Rights Reserved.
706 *
707 * Use of this source code is governed by an MIT-style license that can be
708 * found in the LICENSE file at https://angular.io/license
709 */
710// Privately exported for the grid-list harness.
711const ɵTileCoordinator = TileCoordinator;
712
713/**
714 * @license
715 * Copyright Google LLC All Rights Reserved.
716 *
717 * Use of this source code is governed by an MIT-style license that can be
718 * found in the LICENSE file at https://angular.io/license
719 */
720
721/**
722 * Generated bundle index. Do not edit.
723 */
724
725export { MatGridAvatarCssMatStyler, MatGridList, MatGridListModule, MatGridTile, MatGridTileFooterCssMatStyler, MatGridTileHeaderCssMatStyler, MatGridTileText, ɵTileCoordinator };
726//# sourceMappingURL=grid-list.mjs.map