123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- /**
- * Class for determining, from a list of tiles, the (row, col) position of each of those tiles
- * in the grid. This is necessary (rather than just rendering the tiles in normal document flow)
- * because the tiles can have a rowspan.
- *
- * The positioning algorithm greedily places each tile as soon as it encounters a gap in the grid
- * large enough to accommodate it so that the tiles still render in the same order in which they
- * are given.
- *
- * The basis of the algorithm is the use of an array to track the already placed tiles. Each
- * element of the array corresponds to a column, and the value indicates how many cells in that
- * column are already occupied; zero indicates an empty cell. Moving "down" to the next row
- * decrements each value in the tracking array (indicating that the column is one cell closer to
- * being free).
- *
- * @docs-private
- */
- class TileCoordinator {
- /** Tracking array (see class description). */
- tracker;
- /** Index at which the search for the next gap will start. */
- columnIndex = 0;
- /** The current row index. */
- rowIndex = 0;
- /** Gets the total number of rows occupied by tiles */
- get rowCount() {
- return this.rowIndex + 1;
- }
- /**
- * Gets the total span of rows occupied by tiles.
- * Ex: A list with 1 row that contains a tile with rowspan 2 will have a total rowspan of 2.
- */
- get rowspan() {
- const lastRowMax = Math.max(...this.tracker);
- // if any of the tiles has a rowspan that pushes it beyond the total row count,
- // add the difference to the rowcount
- return lastRowMax > 1 ? this.rowCount + lastRowMax - 1 : this.rowCount;
- }
- /** The computed (row, col) position of each tile (the output). */
- positions;
- /**
- * Updates the tile positions.
- * @param numColumns Amount of columns in the grid.
- * @param tiles Tiles to be positioned.
- */
- update(numColumns, tiles) {
- this.columnIndex = 0;
- this.rowIndex = 0;
- this.tracker = new Array(numColumns);
- this.tracker.fill(0, 0, this.tracker.length);
- this.positions = tiles.map(tile => this._trackTile(tile));
- }
- /** Calculates the row and col position of a tile. */
- _trackTile(tile) {
- // Find a gap large enough for this tile.
- const gapStartIndex = this._findMatchingGap(tile.colspan);
- // Place tile in the resulting gap.
- this._markTilePosition(gapStartIndex, tile);
- // The next time we look for a gap, the search will start at columnIndex, which should be
- // immediately after the tile that has just been placed.
- this.columnIndex = gapStartIndex + tile.colspan;
- return new TilePosition(this.rowIndex, gapStartIndex);
- }
- /** Finds the next available space large enough to fit the tile. */
- _findMatchingGap(tileCols) {
- if (tileCols > this.tracker.length && (typeof ngDevMode === 'undefined' || ngDevMode)) {
- throw Error(`mat-grid-list: tile with colspan ${tileCols} is wider than ` +
- `grid with cols="${this.tracker.length}".`);
- }
- // Start index is inclusive, end index is exclusive.
- let gapStartIndex = -1;
- let gapEndIndex = -1;
- // Look for a gap large enough to fit the given tile. Empty spaces are marked with a zero.
- do {
- // If we've reached the end of the row, go to the next row.
- if (this.columnIndex + tileCols > this.tracker.length) {
- this._nextRow();
- gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
- gapEndIndex = this._findGapEndIndex(gapStartIndex);
- continue;
- }
- gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
- // If there are no more empty spaces in this row at all, move on to the next row.
- if (gapStartIndex == -1) {
- this._nextRow();
- gapStartIndex = this.tracker.indexOf(0, this.columnIndex);
- gapEndIndex = this._findGapEndIndex(gapStartIndex);
- continue;
- }
- gapEndIndex = this._findGapEndIndex(gapStartIndex);
- // If a gap large enough isn't found, we want to start looking immediately after the current
- // gap on the next iteration.
- this.columnIndex = gapStartIndex + 1;
- // Continue iterating until we find a gap wide enough for this tile. Since gapEndIndex is
- // exclusive, gapEndIndex is 0 means we didn't find a gap and should continue.
- } while (gapEndIndex - gapStartIndex < tileCols || gapEndIndex == 0);
- // If we still didn't manage to find a gap, ensure that the index is
- // at least zero so the tile doesn't get pulled out of the grid.
- return Math.max(gapStartIndex, 0);
- }
- /** Move "down" to the next row. */
- _nextRow() {
- this.columnIndex = 0;
- this.rowIndex++;
- // Decrement all spaces by one to reflect moving down one row.
- for (let i = 0; i < this.tracker.length; i++) {
- this.tracker[i] = Math.max(0, this.tracker[i] - 1);
- }
- }
- /**
- * Finds the end index (exclusive) of a gap given the index from which to start looking.
- * The gap ends when a non-zero value is found.
- */
- _findGapEndIndex(gapStartIndex) {
- for (let i = gapStartIndex + 1; i < this.tracker.length; i++) {
- if (this.tracker[i] != 0) {
- return i;
- }
- }
- // The gap ends with the end of the row.
- return this.tracker.length;
- }
- /** Update the tile tracker to account for the given tile in the given space. */
- _markTilePosition(start, tile) {
- for (let i = 0; i < tile.colspan; i++) {
- this.tracker[start + i] = tile.rowspan;
- }
- }
- }
- /**
- * Simple data structure for tile position (row, col).
- * @docs-private
- */
- class TilePosition {
- row;
- col;
- constructor(row, col) {
- this.row = row;
- this.col = col;
- }
- }
- // Privately exported for the grid-list harness.
- const ɵTileCoordinator = TileCoordinator;
- export { TileCoordinator as T, ɵTileCoordinator as ɵ };
- //# sourceMappingURL=public-api-BoO5eSq-.mjs.map
|