UNPKG

@blare/angular2gridster

Version:

[![npm version](https://badge.fury.io/js/angular2gridster.svg)](https://badge.fury.io/js/angular2gridster)

1,441 lines (1,438 loc) 535 kB
import { __spread, __assign, __values } from 'tslib'; import { Injectable, Component, ElementRef, ViewChild, NgZone, Input, Output, EventEmitter, ChangeDetectionStrategy, HostBinding, ViewEncapsulation, Inject, Directive, NgModule } from '@angular/core'; import { Subject, merge, of, fromEvent, Subscription } from 'rxjs'; import { debounceTime, takeUntil, switchMap, map, scan, filter, share, tap, distinctUntilChanged, publish, take, skip } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** @type {?} */ var utils = { setCssElementPosition: function ($element, position) { $element.style.left = position.x + 'px'; $element.style.top = position.y + 'px'; }, resetCSSElementPosition: function ($element) { $element.style.left = ''; $element.style.top = ''; }, setTransform: function ($element, position) { /** @type {?} */ var left = position.x; /** @type {?} */ var top = position.y; // Replace unitless items with px /** @type {?} */ var translate = "translate(" + left + "px," + top + "px)"; $element.style['transform'] = translate; $element.style['WebkitTransform'] = translate; $element.style['MozTransform'] = translate; $element.style['msTransform'] = translate; $element.style['OTransform'] = translate; }, resetTransform: function ($element) { $element.style['transform'] = ''; $element.style['WebkitTransform'] = ''; $element.style['MozTransform'] = ''; $element.style['msTransform'] = ''; $element.style['OTransform'] = ''; }, clearSelection: function () { if (document['selection']) { document['selection'].empty(); } else if (window.getSelection) { window.getSelection().removeAllRanges(); } }, isElementFitContainer: function (element, containerEl) { /** @type {?} */ var containerRect = containerEl.getBoundingClientRect(); /** @type {?} */ var elRect = element.getBoundingClientRect(); return elRect.left > containerRect.left && elRect.right < containerRect.right && elRect.top > containerRect.top && elRect.bottom < containerRect.bottom; }, isElementIntersectContainer: function (element, containerEl) { /** @type {?} */ var containerRect = containerEl.getBoundingClientRect(); /** @type {?} */ var elRect = element.getBoundingClientRect(); /** @type {?} */ var elWidth = elRect.right - elRect.left; /** @type {?} */ var elHeight = elRect.bottom - elRect.top; return (elRect.left + (elWidth / 2)) > containerRect.left && (elRect.right - (elWidth / 2)) < containerRect.right && (elRect.top + (elHeight / 2)) > containerRect.top && (elRect.bottom - (elHeight / 2)) < containerRect.bottom; }, isElementTouchContainer: function (element, containerEl) { /** @type {?} */ var containerRect = containerEl.getBoundingClientRect(); /** @type {?} */ var elRect = element.getBoundingClientRect(); return elRect.right > containerRect.left && elRect.bottom > containerRect.top && elRect.left < containerRect.right && elRect.top < containerRect.bottom; }, isCursorAboveElement: function (event, element) { /** @type {?} */ var elRect = element.getBoundingClientRect(); return event.pageX > elRect.left && event.pageX < elRect.right && event.pageY > elRect.top && event.pageY < elRect.bottom; }, getElementOuterHeight: function ($element) { /** @type {?} */ var styleObj = window.getComputedStyle($element); // NOTE: Manually calculating height because IE's `clientHeight` isn't always // reliable. return parseFloat(styleObj.getPropertyValue('height')) + parseFloat(styleObj.getPropertyValue('padding-top')) + parseFloat(styleObj.getPropertyValue('padding-bottom')); }, getRelativeCoordinates: function (element, parentElement) { /** @type {?} */ var parentElementRect = parentElement.getBoundingClientRect(); /** @type {?} */ var elementRect = element.getBoundingClientRect(); return { top: elementRect.top - parentElementRect.top, left: elementRect.left - parentElementRect.left }; }, getScrollableContainer: /** * @param {?} node * @return {?} */ function (node) { /** @type {?} */ var regex = /(auto|scroll)/; /** @type {?} */ var parents = function (_node, ps) { if (_node.parentNode === null) { return ps; } return parents(_node.parentNode, ps.concat([_node])); }; /** @type {?} */ var style = function (_node, prop) { return getComputedStyle(_node, null).getPropertyValue(prop); }; /** @type {?} */ var overflow = function (_node) { return (style(_node, 'overflow') + style(_node, 'overflow-y') + style(_node, 'overflow-x')); }; /** @type {?} */ var scroll = function (_node) { return regex.test(overflow(_node)); }; /* eslint-disable consistent-return */ /** @type {?} */ var scrollParent = function (_node) { if (!(_node instanceof HTMLElement || _node instanceof SVGElement)) { return; } /** @type {?} */ var ps = parents(_node.parentNode, []); for (var i = 0; i < ps.length; i += 1) { if (scroll(ps[i])) { return ps[i]; } } return document.scrollingElement || document.documentElement; }; return scrollParent(node); } }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc */ /** @type {?} */ var GridCol = function (lanes) { for (var i = 0; i < lanes; i++) { this.push(null); } }; // Extend the Array prototype GridCol.prototype = []; /** * A GridList manages the two-dimensional positions from a list of items, * within a virtual matrix. * * The GridList's main function is to convert the item positions from one * grid size to another, maintaining as much of their order as possible. * * The GridList's second function is to handle collisions when moving an item * over another. * * The positioning algorithm places items in columns. Starting from left to * right, going through each column top to bottom. * * The size of an item is expressed using the number of cols and rows it * takes up within the grid (w and h) * * The position of an item is express using the col and row position within * the grid (x and y) * * An item is an object of structure: * { * w: 3, h: 1, * x: 0, y: 1 * } */ var /** * A GridList manages the two-dimensional positions from a list of items, * within a virtual matrix. * * The GridList's main function is to convert the item positions from one * grid size to another, maintaining as much of their order as possible. * * The GridList's second function is to handle collisions when moving an item * over another. * * The positioning algorithm places items in columns. Starting from left to * right, going through each column top to bottom. * * The size of an item is expressed using the number of cols and rows it * takes up within the grid (w and h) * * The position of an item is express using the col and row position within * the grid (x and y) * * An item is an object of structure: * { * w: 3, h: 1, * x: 0, y: 1 * } */ GridList = /** @class */ (function () { function GridList(items, options) { this.options = options; this.items = items; this.adjustSizeOfItems(); this.generateGrid(); } /** * Illustrates grid as text-based table, using a number identifier for each * item. E.g. * * #| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 * -------------------------------------------- * 0| 00 02 03 04 04 06 08 08 08 12 12 13 14 16 * 1| 01 -- 03 05 05 07 09 10 11 11 -- 13 15 -- * * Warn: Does not work if items don't have a width or height specified * besides their position in the grid. */ /** * Illustrates grid as text-based table, using a number identifier for each * item. E.g. * * #| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 * -------------------------------------------- * 0| 00 02 03 04 04 06 08 08 08 12 12 13 14 16 * 1| 01 -- 03 05 05 07 09 10 11 11 -- 13 15 -- * * Warn: Does not work if items don't have a width or height specified * besides their position in the grid. * @return {?} */ GridList.prototype.toString = /** * Illustrates grid as text-based table, using a number identifier for each * item. E.g. * * #| 0 1 2 3 4 5 6 7 8 9 10 11 12 13 * -------------------------------------------- * 0| 00 02 03 04 04 06 08 08 08 12 12 13 14 16 * 1| 01 -- 03 05 05 07 09 10 11 11 -- 13 15 -- * * Warn: Does not work if items don't have a width or height specified * besides their position in the grid. * @return {?} */ function () { /** @type {?} */ var widthOfGrid = this.grid.length; /** @type {?} */ var output = '\n #|'; /** @type {?} */ var border = '\n --'; /** @type {?} */ var item; /** @type {?} */ var i; /** @type {?} */ var j; // Render the table header for (i = 0; i < widthOfGrid; i++) { output += ' ' + this.padNumber(i, ' '); border += '---'; } output += border; // Render table contents row by row, as we go on the y axis for (i = 0; i < this.options.lanes; i++) { output += '\n' + this.padNumber(i, ' ') + '|'; for (j = 0; j < widthOfGrid; j++) { output += ' '; item = this.grid[j][i]; output += item ? this.padNumber(this.items.indexOf(item), '0') : '--'; } } output += '\n'; return output; }; /** * @param {?} name * @param {?} value * @return {?} */ GridList.prototype.setOption = /** * @param {?} name * @param {?} value * @return {?} */ function (name, value) { this.options[name] = value; }; /** * Build the grid structure from scratch, with the current item positions */ /** * Build the grid structure from scratch, with the current item positions * @return {?} */ GridList.prototype.generateGrid = /** * Build the grid structure from scratch, with the current item positions * @return {?} */ function () { /** @type {?} */ var i; this.resetGrid(); for (i = 0; i < this.items.length; i++) { this.markItemPositionToGrid(this.items[i]); } }; /** * @param {?} lanes * @return {?} */ GridList.prototype.resizeGrid = /** * @param {?} lanes * @return {?} */ function (lanes) { /** @type {?} */ var currentColumn = 0; this.options.lanes = lanes; this.adjustSizeOfItems(); this.sortItemsByPosition(); this.resetGrid(); // The items will be sorted based on their index within the this.items array, // that is their "1d position" for (var i = 0; i < this.items.length; i++) { /** @type {?} */ var item = this.items[i]; /** @type {?} */ var position = this.getItemPosition(item); this.updateItemPosition(item, this.findPositionForItem(item, { x: currentColumn, y: 0 })); // New items should never be placed to the left of previous items currentColumn = Math.max(currentColumn, position.x); } this.pullItemsToLeft(); }; /** * This method has two options for the position we want for the item: * - Starting from a certain row/column number and only looking for * positions to its right * - Accepting positions for a certain row number only (use-case: items * being shifted to the left/right as a result of collisions) * * @param Object item * @param Object start Position from which to start * the search. * @param number [fixedRow] If provided, we're going to try to find a * position for the new item on it. If doesn't fit there, we're going * to put it on the first row. * * @returns Array x and y. */ /** * This method has two options for the position we want for the item: * - Starting from a certain row/column number and only looking for * positions to its right * - Accepting positions for a certain row number only (use-case: items * being shifted to the left/right as a result of collisions) * * @param {?} item * @param {?} start * @param {?=} fixedRow * @return {?} Array x and y. */ GridList.prototype.findPositionForItem = /** * This method has two options for the position we want for the item: * - Starting from a certain row/column number and only looking for * positions to its right * - Accepting positions for a certain row number only (use-case: items * being shifted to the left/right as a result of collisions) * * @param {?} item * @param {?} start * @param {?=} fixedRow * @return {?} Array x and y. */ function (item, start, fixedRow) { /** @type {?} */ var x; /** @type {?} */ var y; /** @type {?} */ var position; // Start searching for a position from the horizontal position of the // rightmost item from the grid for (x = start.x; x < this.grid.length; x++) { if (fixedRow !== undefined) { position = [x, fixedRow]; if (this.itemFitsAtPosition(item, position)) { return position; } } else { for (y = start.y; y < this.options.lanes; y++) { position = [x, y]; if (this.itemFitsAtPosition(item, position)) { return position; } } } } // If we've reached this point, we need to start a new column /** @type {?} */ var newCol = this.grid.length; /** @type {?} */ var newRow = 0; if (fixedRow !== undefined && this.itemFitsAtPosition(item, [newCol, fixedRow])) { newRow = fixedRow; } return [newCol, newRow]; }; /** * @param {?} item * @param {?} newPosition * @param {?} size * @return {?} */ GridList.prototype.moveAndResize = /** * @param {?} item * @param {?} newPosition * @param {?} size * @return {?} */ function (item, newPosition, size) { /** @type {?} */ var position = this.getItemPosition({ x: newPosition[0], y: newPosition[1], w: item.w, h: item.h }); /** @type {?} */ var width = size.w || item.w; /** @type {?} */ var height = size.h || item.h; this.updateItemPosition(item, [position.x, position.y]); this.updateItemSize(item, width, height); this.resolveCollisions(item); }; /** * @param {?} item * @param {?} newPosition * @return {?} */ GridList.prototype.moveItemToPosition = /** * @param {?} item * @param {?} newPosition * @return {?} */ function (item, newPosition) { /** @type {?} */ var position = this.getItemPosition({ x: newPosition[0], y: newPosition[1], w: item.w, h: item.h }); this.updateItemPosition(item, [position.x, position.y]); this.resolveCollisions(item); }; /** * Resize an item and resolve collisions. * * @param Object item A reference to an item that's part of the grid. * @param Object size * @param number [size.w=item.w] The new width. * @param number [size.h=item.h] The new height. */ /** * Resize an item and resolve collisions. * * @param {?} item * @param {?} size * @return {?} */ GridList.prototype.resizeItem = /** * Resize an item and resolve collisions. * * @param {?} item * @param {?} size * @return {?} */ function (item, size) { /** @type {?} */ var width = size.w || item.w; /** @type {?} */ var height = size.h || item.h; this.updateItemSize(item, width, height); this.pullItemsToLeft(item); }; /** * Compare the current items against a previous snapshot and return only * the ones that changed their attributes in the meantime. This includes both * position (x, y) and size (w, h) * * Each item that is returned is not the GridListItem but the helper that holds GridListItem * and list of changed properties. */ /** * Compare the current items against a previous snapshot and return only * the ones that changed their attributes in the meantime. This includes both * position (x, y) and size (w, h) * * Each item that is returned is not the GridListItem but the helper that holds GridListItem * and list of changed properties. * @param {?} initialItems * @param {?=} breakpoint * @return {?} */ GridList.prototype.getChangedItems = /** * Compare the current items against a previous snapshot and return only * the ones that changed their attributes in the meantime. This includes both * position (x, y) and size (w, h) * * Each item that is returned is not the GridListItem but the helper that holds GridListItem * and list of changed properties. * @param {?} initialItems * @param {?=} breakpoint * @return {?} */ function (initialItems, breakpoint) { return this.items .map(function (item) { /** @type {?} */ var changes = []; /** @type {?} */ var oldValues = {}; /** @type {?} */ var initItem = initialItems.find(function (initItm) { return initItm.$element === item.$element; }); if (!initItem) { return { item: item, changes: ['x', 'y', 'w', 'h'], isNew: true }; } /** @type {?} */ var oldX = initItem.getValueX(breakpoint); if (item.getValueX(breakpoint) !== oldX) { changes.push('x'); if (oldX || oldX === 0) { oldValues.x = oldX; } } /** @type {?} */ var oldY = initItem.getValueY(breakpoint); if (item.getValueY(breakpoint) !== oldY) { changes.push('y'); if (oldY || oldY === 0) { oldValues.y = oldY; } } if (item.getValueW(breakpoint) !== initItem.getValueW(breakpoint)) { changes.push('w'); oldValues.w = initItem.w; } if (item.getValueH(breakpoint) !== initItem.getValueH(breakpoint)) { changes.push('h'); oldValues.h = initItem.h; } return { item: item, oldValues: oldValues, changes: changes, isNew: false }; }) .filter(function (itemChange) { return itemChange.changes.length; }); }; /** * @param {?} item * @return {?} */ GridList.prototype.resolveCollisions = /** * @param {?} item * @return {?} */ function (item) { if (!this.tryToResolveCollisionsLocally(item)) { this.pullItemsToLeft(item); } if (this.options.floating) { this.pullItemsToLeft(); } else if (this.getItemsCollidingWithItem(item).length) { this.pullItemsToLeft(); } }; /** * @param {?=} fixedItem * @return {?} */ GridList.prototype.pushCollidingItems = /** * @param {?=} fixedItem * @return {?} */ function (fixedItem) { var _this = this; // Start a fresh grid with the fixed item already placed inside this.sortItemsByPosition(); this.resetGrid(); this.generateGrid(); this.items .filter(function (item) { return !_this.isItemFloating(item) && item !== fixedItem; }) .forEach(function (item) { if (!_this.tryToResolveCollisionsLocally(item)) { _this.pullItemsToLeft(item); } }); }; /** * Build the grid from scratch, by using the current item positions and * pulling them as much to the left as possible, removing as space between * them as possible. * * If a "fixed item" is provided, its position will be kept intact and the * rest of the items will be layed around it. */ /** * Build the grid from scratch, by using the current item positions and * pulling them as much to the left as possible, removing as space between * them as possible. * * If a "fixed item" is provided, its position will be kept intact and the * rest of the items will be layed around it. * @param {?=} fixedItem * @return {?} */ GridList.prototype.pullItemsToLeft = /** * Build the grid from scratch, by using the current item positions and * pulling them as much to the left as possible, removing as space between * them as possible. * * If a "fixed item" is provided, its position will be kept intact and the * rest of the items will be layed around it. * @param {?=} fixedItem * @return {?} */ function (fixedItem) { var _this = this; if (this.options.direction === 'none') { return; } // Start a fresh grid with the fixed item already placed inside this.sortItemsByPosition(); this.resetGrid(); // Start the grid with the fixed item as the first positioned item if (fixedItem) { /** @type {?} */ var fixedPosition = this.getItemPosition(fixedItem); this.updateItemPosition(fixedItem, [ fixedPosition.x, fixedPosition.y ]); } this.items .filter(function (item) { return !item.dragAndDrop && item !== fixedItem; }) .forEach(function (item) { /** @type {?} */ var fixedPosition = _this.getItemPosition(item); _this.updateItemPosition(item, [ fixedPosition.x, fixedPosition.y ]); }); for (var i = 0; i < this.items.length; i++) { /** @type {?} */ var item = this.items[i]; /** @type {?} */ var position = this.getItemPosition(item); // The fixed item keeps its exact position if ((fixedItem && item === fixedItem) || !item.dragAndDrop || (!this.options.floating && this.isItemFloating(item) && !this.getItemsCollidingWithItem(item).length)) { continue; } /** @type {?} */ var x = this.findLeftMostPositionForItem(item); /** @type {?} */ var newPosition = this.findPositionForItem(item, { x: x, y: 0 }, position.y); this.updateItemPosition(item, newPosition); } }; /** * @param {?} x * @param {?} y * @param {?} w * @param {?} h * @param {?=} item * @return {?} */ GridList.prototype.isOverFixedArea = /** * @param {?} x * @param {?} y * @param {?} w * @param {?} h * @param {?=} item * @return {?} */ function (x, y, w, h, item) { if (item === void 0) { item = null; } /** @type {?} */ var itemData = { x: x, y: y, w: w, h: h }; if (this.options.direction !== 'horizontal') { itemData = { x: y, y: x, w: h, h: w }; } for (var i = itemData.x; i < itemData.x + itemData.w; i++) { for (var j = itemData.y; j < itemData.y + itemData.h; j++) { if (this.grid[i] && this.grid[i][j] && this.grid[i][j] !== item && !this.grid[i][j].dragAndDrop) { return true; } } } return false; }; /** * @param {?} item * @param {?} newPosition * @return {?} */ GridList.prototype.checkItemAboveEmptyArea = /** * @param {?} item * @param {?} newPosition * @return {?} */ function (item, newPosition) { /** @type {?} */ var itemData = { x: newPosition.x, y: newPosition.y, w: item.w, h: item.h }; if (!item.itemPrototype && item.x === newPosition.x && item.y === newPosition.y) { return true; } if (this.options.direction === 'horizontal') { itemData = { x: newPosition.y, y: newPosition.x, w: itemData.h, h: itemData.w }; } return !this.checkItemsInArea(itemData.y, itemData.y + itemData.h - 1, itemData.x, itemData.x + itemData.w - 1, item); }; /** * @param {?} options * @return {?} */ GridList.prototype.fixItemsPositions = /** * @param {?} options * @return {?} */ function (options) { var _this = this; // items with x, y that fits gird with size of options.lanes /** @type {?} */ var validItems = this.items .filter(function (item) { return item.itemComponent; }) .filter(function (item) { return _this.isItemValidForGrid(item, options); }); // items that x, y must be generated /** @type {?} */ var invalidItems = this.items .filter(function (item) { return item.itemComponent; }) .filter(function (item) { return !_this.isItemValidForGrid(item, options); }); /** @type {?} */ var gridList = new GridList([], options); // put items with defined positions to the grid gridList.items = validItems.map(function (item) { return item.copyForBreakpoint(options.breakpoint); }); gridList.generateGrid(); invalidItems.forEach(function (item) { // TODO: check if this change does not broke anything // const itemCopy = item.copy(); /** @type {?} */ var itemCopy = item.copyForBreakpoint(options.breakpoint); /** @type {?} */ var position = gridList.findPositionForItem(itemCopy, { x: 0, y: 0 }); gridList.items.push(itemCopy); gridList.setItemPosition(itemCopy, position); gridList.markItemPositionToGrid(itemCopy); }); gridList.pullItemsToLeft(); gridList.pushCollidingItems(); this.items.forEach(function (itm) { /** @type {?} */ var cachedItem = gridList.items.filter(function (cachedItm) { return cachedItm.$element === itm.$element; })[0]; itm.setValueX(cachedItem.x, options.breakpoint); itm.setValueY(cachedItem.y, options.breakpoint); itm.setValueW(cachedItem.w, options.breakpoint); itm.setValueH(cachedItem.h, options.breakpoint); itm.autoSize = cachedItem.autoSize; }); }; /** * @param {?} item * @return {?} */ GridList.prototype.deleteItemPositionFromGrid = /** * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var position = this.getItemPosition(item); /** @type {?} */ var x; /** @type {?} */ var y; for (x = position.x; x < position.x + position.w; x++) { // It can happen to try to remove an item from a position not generated // in the grid, probably when loading a persisted grid of items. No need // to create a column to be able to remove something from it, though if (!this.grid[x]) { continue; } for (y = position.y; y < position.y + position.h; y++) { // Don't clear the cell if it's been occupied by a different widget in // the meantime (e.g. when an item has been moved over this one, and // thus by continuing to clear this item's previous position you would // cancel the first item's move, leaving it without any position even) if (this.grid[x][y] === item) { this.grid[x][y] = null; } } } }; /** * @param {?} item * @return {?} */ GridList.prototype.isItemFloating = /** * @param {?} item * @return {?} */ function (item) { if (item.itemComponent && item.itemComponent.isDragging) { return false; } /** @type {?} */ var position = this.getItemPosition(item); if (position.x === 0) { return false; } /** @type {?} */ var rowBelowItem = this.grid[position.x - 1]; return (rowBelowItem || []) .slice(position.y, position.y + position.h) .reduce(function (isFloating, cellItem) { return isFloating && !cellItem; }, true); }; /** * @param {?} item * @param {?} options * @return {?} */ GridList.prototype.isItemValidForGrid = /** * @param {?} item * @param {?} options * @return {?} */ function (item, options) { /** @type {?} */ var itemData = options.direction === 'horizontal' ? { x: item.getValueY(options.breakpoint), y: item.getValueX(options.breakpoint), w: item.getValueH(options.breakpoint), h: Math.min(item.getValueW(this.options.breakpoint), options.lanes) } : { x: item.getValueX(options.breakpoint), y: item.getValueY(options.breakpoint), w: Math.min(item.getValueW(this.options.breakpoint), options.lanes), h: item.getValueH(options.breakpoint) }; return (typeof itemData.x === 'number' && typeof itemData.y === 'number' && itemData.x + itemData.w <= options.lanes); }; /** * @param {?} width * @param {?} height * @return {?} */ GridList.prototype.findDefaultPositionHorizontal = /** * @param {?} width * @param {?} height * @return {?} */ function (width, height) { var e_1, _a; try { for (var _b = __values(this.grid), _c = _b.next(); !_c.done; _c = _b.next()) { var col = _c.value; /** @type {?} */ var colIdx = this.grid.indexOf(col); /** @type {?} */ var rowIdx = 0; while (rowIdx < col.length - height + 1) { if (!this.checkItemsInArea(colIdx, colIdx + width - 1, rowIdx, rowIdx + height - 1)) { return [colIdx, rowIdx]; } rowIdx++; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } return [this.grid.length, 0]; }; /** * @param {?} width * @param {?} height * @return {?} */ GridList.prototype.findDefaultPositionVertical = /** * @param {?} width * @param {?} height * @return {?} */ function (width, height) { var e_2, _a; try { for (var _b = __values(this.grid), _c = _b.next(); !_c.done; _c = _b.next()) { var row = _c.value; /** @type {?} */ var rowIdx = this.grid.indexOf(row); /** @type {?} */ var colIdx = 0; while (colIdx < row.length - width + 1) { if (!this.checkItemsInArea(rowIdx, rowIdx + height - 1, colIdx, colIdx + width - 1)) { return [colIdx, rowIdx]; } colIdx++; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_2) throw e_2.error; } } return [0, this.grid.length]; }; /** * @param {?} rowStart * @param {?} rowEnd * @param {?} colStart * @param {?} colEnd * @param {?=} item * @return {?} */ GridList.prototype.checkItemsInArea = /** * @param {?} rowStart * @param {?} rowEnd * @param {?} colStart * @param {?} colEnd * @param {?=} item * @return {?} */ function (rowStart, rowEnd, colStart, colEnd, item) { for (var i = rowStart; i <= rowEnd; i++) { for (var j = colStart; j <= colEnd; j++) { if (this.grid[i] && this.grid[i][j] && (item ? this.grid[i][j] !== item : true)) { return true; } } } return false; }; /** * @return {?} */ GridList.prototype.sortItemsByPosition = /** * @return {?} */ function () { var _this = this; this.items.sort(function (item1, item2) { /** @type {?} */ var position1 = _this.getItemPosition(item1); /** @type {?} */ var position2 = _this.getItemPosition(item2); // Try to preserve columns. if (position1.x !== position2.x) { return position1.x - position2.x; } if (position1.y !== position2.y) { return position1.y - position2.y; } // The items are placed on the same position. return 0; }); }; /** * Some items can have 100% height or 100% width. Those dimmensions are * expressed as 0. We need to ensure a valid width and height for each of * those items as the number of items per lane. */ /** * Some items can have 100% height or 100% width. Those dimmensions are * expressed as 0. We need to ensure a valid width and height for each of * those items as the number of items per lane. * @return {?} */ GridList.prototype.adjustSizeOfItems = /** * Some items can have 100% height or 100% width. Those dimmensions are * expressed as 0. We need to ensure a valid width and height for each of * those items as the number of items per lane. * @return {?} */ function () { for (var i = 0; i < this.items.length; i++) { /** @type {?} */ var item = this.items[i]; // This can happen only the first time items are checked. // We need the property to have a value for all the items so that the // `cloneItems` method will merge the properties properly. If we only set // it to the items that need it then the following can happen: // // cloneItems([{id: 1, autoSize: true}, {id: 2}], // [{id: 2}, {id: 1, autoSize: true}]); // // will result in // // [{id: 1, autoSize: true}, {id: 2, autoSize: true}] if (item.autoSize === undefined) { item.autoSize = item.w === 0 || item.h === 0; } if (item.autoSize) { if (this.options.direction === 'horizontal') { item.h = this.options.lanes; } else { item.w = this.options.lanes; } } } }; /** * @return {?} */ GridList.prototype.resetGrid = /** * @return {?} */ function () { this.grid = []; }; /** * Check that an item wouldn't overlap with another one if placed at a * certain position within the grid */ /** * Check that an item wouldn't overlap with another one if placed at a * certain position within the grid * @param {?} item * @param {?} newPosition * @return {?} */ GridList.prototype.itemFitsAtPosition = /** * Check that an item wouldn't overlap with another one if placed at a * certain position within the grid * @param {?} item * @param {?} newPosition * @return {?} */ function (item, newPosition) { /** @type {?} */ var position = this.getItemPosition(item); /** @type {?} */ var x; /** @type {?} */ var y; // No coordonate can be negative if (newPosition[0] < 0 || newPosition[1] < 0) { return false; } // Make sure the item isn't larger than the entire grid if (newPosition[1] + Math.min(position.h, this.options.lanes) > this.options.lanes) { return false; } if (this.isOverFixedArea(item.x, item.y, item.w, item.h)) { return false; } // Make sure the position doesn't overlap with an already positioned // item. for (x = newPosition[0]; x < newPosition[0] + position.w; x++) { /** @type {?} */ var col = this.grid[x]; // Surely a column that hasn't even been created yet is available if (!col) { continue; } for (y = newPosition[1]; y < newPosition[1] + position.h; y++) { // Any space occupied by an item can continue to be occupied by the // same item. if (col[y] && col[y] !== item) { return false; } } } return true; }; /** * @param {?} item * @param {?} position * @return {?} */ GridList.prototype.updateItemPosition = /** * @param {?} item * @param {?} position * @return {?} */ function (item, position) { if (item.x !== null && item.y !== null) { this.deleteItemPositionFromGrid(item); } this.setItemPosition(item, position); this.markItemPositionToGrid(item); }; /** * @param Object item A reference to a grid item. * @param number width The new width. * @param number height The new height. */ /** * @param {?} item * @param {?} width * @param {?} height * @return {?} */ GridList.prototype.updateItemSize = /** * @param {?} item * @param {?} width * @param {?} height * @return {?} */ function (item, width, height) { if (item.x !== null && item.y !== null) { this.deleteItemPositionFromGrid(item); } item.w = width; item.h = height; this.markItemPositionToGrid(item); }; /** * Mark the grid cells that are occupied by an item. This prevents items * from overlapping in the grid */ /** * Mark the grid cells that are occupied by an item. This prevents items * from overlapping in the grid * @param {?} item * @return {?} */ GridList.prototype.markItemPositionToGrid = /** * Mark the grid cells that are occupied by an item. This prevents items * from overlapping in the grid * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var position = this.getItemPosition(item); /** @type {?} */ var x; /** @type {?} */ var y; // Ensure that the grid has enough columns to accomodate the current item. this.ensureColumns(position.x + position.w); for (x = position.x; x < position.x + position.w; x++) { for (y = position.y; y < position.y + position.h; y++) { this.grid[x][y] = item; } } }; /** * Ensure that the grid has at least N columns available. */ /** * Ensure that the grid has at least N columns available. * @param {?} N * @return {?} */ GridList.prototype.ensureColumns = /** * Ensure that the grid has at least N columns available. * @param {?} N * @return {?} */ function (N) { for (var i = 0; i < N; i++) { if (!this.grid[i]) { this.grid.push(new GridCol(this.options.lanes)); } } }; /** * @param {?} item * @return {?} */ GridList.prototype.getItemsCollidingWithItem = /** * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var collidingItems = []; for (var i = 0; i < this.items.length; i++) { if (item !== this.items[i] && this.itemsAreColliding(item, this.items[i])) { collidingItems.push(i); } } return collidingItems; }; /** * @param {?} item1 * @param {?} item2 * @return {?} */ GridList.prototype.itemsAreColliding = /** * @param {?} item1 * @param {?} item2 * @return {?} */ function (item1, item2) { /** @type {?} */ var position1 = this.getItemPosition(item1); /** @type {?} */ var position2 = this.getItemPosition(item2); return !(position2.x >= position1.x + position1.w || position2.x + position2.w <= position1.x || position2.y >= position1.y + position1.h || position2.y + position2.h <= position1.y); }; /** * Attempt to resolve the collisions after moving an item over one or more * other items within the grid, by shifting the position of the colliding * items around the moving one. This might result in subsequent collisions, * in which case we will revert all position permutations. To be able to * revert to the initial item positions, we create a virtual grid in the * process */ /** * Attempt to resolve the collisions after moving an item over one or more * other items within the grid, by shifting the position of the colliding * items around the moving one. This might result in subsequent collisions, * in which case we will revert all position permutations. To be able to * revert to the initial item positions, we create a virtual grid in the * process * @param {?} item * @return {?} */ GridList.prototype.tryToResolveCollisionsLocally = /** * Attempt to resolve the collisions after moving an item over one or more * other items within the grid, by shifting the position of the colliding * items around the moving one. This might result in subsequent collisions, * in which case we will revert all position permutations. To be able to * revert to the initial item positions, we create a virtual grid in the * process * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var collidingItems = this.getItemsCollidingWithItem(item); if (!collidingItems.length) { return true; } /** @type {?} */ var _gridList = new GridList(this.items.map(function (itm) { return itm.copy(); }), this.options); /** @type {?} */ var leftOfItem; /** @type {?} */ var rightOfItem; /** @type {?} */ var aboveOfItem; /** @type {?} */ var belowOfItem; for (var i = 0; i < collidingItems.length; i++) { /** @type {?} */ var collidingItem = _gridList.items[collidingItems[i]]; /** @type {?} */ var collidingPosition = this.getItemPosition(collidingItem); // We use a simple algorithm for moving items around when collisions occur: // In this prioritized order, we try to move a colliding item around the // moving one: // 1. to its left side // 2. above it // 3. under it // 4. to its right side /** @type {?} */ var position = this.getItemPosition(item); leftOfItem = [ position.x - collidingPosition.w, collidingPosition.y ]; rightOfItem = [position.x + position.w, collidingPosition.y]; aboveOfItem = [ collidingPosition.x, position.y - collidingPosition.h ]; belowOfItem = [collidingPosition.x, position.y + position.h]; if (_gridList.itemFitsAtPosition(collidingItem, leftOfItem)) { _gridList.updateItemPosition(collidingItem, leftOfItem); } else if (_gridList.itemFitsAtPosition(collidingItem, aboveOfItem)) { _gridList.updateItemPosition(collidingItem, aboveOfItem); } else if (_gridList.itemFitsAtPosition(collidingItem, belowOfItem)) { _gridList.updateItemPosition(collidingItem, belowOfItem); } else if (_gridList.itemFitsAtPosition(collidingItem, rightOfItem)) { _gridList.updateItemPosition(collidingItem, rightOfItem); } else { // Collisions failed, we must use the pullItemsToLeft method to arrange // the other items around this item with fixed position. This is our // plan B for when local collision resolving fails. return false; } } // If we reached this point it means we managed to resolve the collisions // from one single iteration, just by moving the colliding items around. So // we accept this scenario and merge the branched-out grid instance into the // original one this.items.forEach(function (itm, idx) { /** @type {?} */ var cachedItem = _gridList.items.filter(function (cachedItm) { return cachedItm.$element === itm.$element; })[0]; itm.x = cachedItem.x; itm.y = cachedItem.y; itm.w = cachedItem.w; itm.h = cachedItem.h; itm.autoSize = cachedItem.autoSize; }); this.generateGrid(); return true; }; /** * When pulling items to the left, we need to find the leftmost position for * an item, with two considerations in mind: * - preserving its current row * - preserving the previous horizontal order between items */ /** * When pulling items to the left, we need to find the leftmost position for * an item, with two considerations in mind: * - preserving its current row * - preserving the previous horizontal order between items * @param {?} item * @return {?} */ GridList.prototype.findLeftMostPositionForItem = /** * When pulling items to the left, we need to find the leftmost position for * an item, with two considerations in mind: * - preserving its current row * - preserving the previous horizontal order between items * @param {?} item * @return {?} */ function (item) { /** @type {?} */ var tail = 0; /** @type {?} */ var position = this.getItemPosition(item); for (var i = 0; i < this.grid.length; i++) { for (var j = position.y; j < position.y + position.h; j++) { /** @type {?} */ var otherItem = this.grid[i][j]; if (!otherItem) { continue; } /** @type {?} */ var otherPosition = this.getItemPosition(otherItem); if (this.items.indexOf(otherItem) < this.items.indexOf(item)) { tail = otherPosition.x + otherPosition.w; } } } return tail; }; /** * @param {?} x * @param {?} y * @return {?} */ GridList.prototype.findItemByPosition = /** * @param {?} x * @param {?} y * @return {?} */ function (x, y) { for (var i = 0; i < this.items.length; i++) {