@blare/angular2gridster
Version:
[](https://badge.fury.io/js/angular2gridster)
1,036 lines • 112 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
/** @type {?} */
const GridCol = function (lanes) {
for (let i = 0; i < lanes; i++) {
this.push(null);
}
};
const ɵ0 = GridCol;
// 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
* }
*/
export class GridList {
/**
* @param {?} items
* @param {?} options
*/
constructor(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.
* @return {?}
*/
toString() {
/** @type {?} */
const widthOfGrid = this.grid.length;
/** @type {?} */
let output = '\n #|';
/** @type {?} */
let border = '\n --';
/** @type {?} */
let item;
/** @type {?} */
let i;
/** @type {?} */
let 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 {?}
*/
setOption(name, value) {
this.options[name] = value;
}
/**
* Build the grid structure from scratch, with the current item positions
* @return {?}
*/
generateGrid() {
/** @type {?} */
let i;
this.resetGrid();
for (i = 0; i < this.items.length; i++) {
this.markItemPositionToGrid(this.items[i]);
}
}
/**
* @param {?} lanes
* @return {?}
*/
resizeGrid(lanes) {
/** @type {?} */
let 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 (let i = 0; i < this.items.length; i++) {
/** @type {?} */
const item = this.items[i];
/** @type {?} */
const 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 {?} item
* @param {?} start
* @param {?=} fixedRow
* @return {?} Array x and y.
*/
findPositionForItem(item, start, fixedRow) {
/** @type {?} */
let x;
/** @type {?} */
let y;
/** @type {?} */
let 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 {?} */
const newCol = this.grid.length;
/** @type {?} */
let newRow = 0;
if (fixedRow !== undefined &&
this.itemFitsAtPosition(item, [newCol, fixedRow])) {
newRow = fixedRow;
}
return [newCol, newRow];
}
/**
* @param {?} item
* @param {?} newPosition
* @param {?} size
* @return {?}
*/
moveAndResize(item, newPosition, size) {
/** @type {?} */
const position = this.getItemPosition({
x: newPosition[0],
y: newPosition[1],
w: item.w,
h: item.h
});
/** @type {?} */
const width = size.w || item.w;
/** @type {?} */
const 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 {?}
*/
moveItemToPosition(item, newPosition) {
/** @type {?} */
const 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 {?} item
* @param {?} size
* @return {?}
*/
resizeItem(item, size) {
/** @type {?} */
const width = size.w || item.w;
/** @type {?} */
const 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.
* @param {?} initialItems
* @param {?=} breakpoint
* @return {?}
*/
getChangedItems(initialItems, breakpoint) {
return this.items
.map((item) => {
/** @type {?} */
const changes = [];
/** @type {?} */
const oldValues = {};
/** @type {?} */
const initItem = initialItems.find(initItm => initItm.$element === item.$element);
if (!initItem) {
return { item, changes: ['x', 'y', 'w', 'h'], isNew: true };
}
/** @type {?} */
const oldX = initItem.getValueX(breakpoint);
if (item.getValueX(breakpoint) !== oldX) {
changes.push('x');
if (oldX || oldX === 0) {
oldValues.x = oldX;
}
}
/** @type {?} */
const 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, oldValues, changes, isNew: false };
})
.filter((itemChange) => {
return itemChange.changes.length;
});
}
/**
* @param {?} item
* @return {?}
*/
resolveCollisions(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 {?}
*/
pushCollidingItems(fixedItem) {
// Start a fresh grid with the fixed item already placed inside
this.sortItemsByPosition();
this.resetGrid();
this.generateGrid();
this.items
.filter(item => !this.isItemFloating(item) && item !== fixedItem)
.forEach(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.
* @param {?=} fixedItem
* @return {?}
*/
pullItemsToLeft(fixedItem) {
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 {?} */
const fixedPosition = this.getItemPosition(fixedItem);
this.updateItemPosition(fixedItem, [
fixedPosition.x,
fixedPosition.y
]);
}
this.items
.filter((item) => {
return !item.dragAndDrop && item !== fixedItem;
})
.forEach((item) => {
/** @type {?} */
const fixedPosition = this.getItemPosition(item);
this.updateItemPosition(item, [
fixedPosition.x,
fixedPosition.y
]);
});
for (let i = 0; i < this.items.length; i++) {
/** @type {?} */
const item = this.items[i];
/** @type {?} */
const 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 {?} */
const x = this.findLeftMostPositionForItem(item);
/** @type {?} */
const 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 {?}
*/
isOverFixedArea(x, y, w, h, item = null) {
/** @type {?} */
let itemData = { x, y, w, h };
if (this.options.direction !== 'horizontal') {
itemData = { x: y, y: x, w: h, h: w };
}
for (let i = itemData.x; i < itemData.x + itemData.w; i++) {
for (let 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 {?}
*/
checkItemAboveEmptyArea(item, newPosition) {
/** @type {?} */
let 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 {?}
*/
fixItemsPositions(options) {
// items with x, y that fits gird with size of options.lanes
/** @type {?} */
const validItems = this.items
.filter((item) => item.itemComponent)
.filter((item) => this.isItemValidForGrid(item, options));
// items that x, y must be generated
/** @type {?} */
const invalidItems = this.items
.filter((item) => item.itemComponent)
.filter((item) => !this.isItemValidForGrid(item, options));
/** @type {?} */
const gridList = new GridList([], options);
// put items with defined positions to the grid
gridList.items = validItems.map((item) => {
return item.copyForBreakpoint(options.breakpoint);
});
gridList.generateGrid();
invalidItems.forEach(item => {
// TODO: check if this change does not broke anything
// const itemCopy = item.copy();
/** @type {?} */
const itemCopy = item.copyForBreakpoint(options.breakpoint);
/** @type {?} */
const 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((itm) => {
/** @type {?} */
const cachedItem = gridList.items.filter(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 {?}
*/
deleteItemPositionFromGrid(item) {
/** @type {?} */
const position = this.getItemPosition(item);
/** @type {?} */
let x;
/** @type {?} */
let 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 {?}
*/
isItemFloating(item) {
if (item.itemComponent && item.itemComponent.isDragging) {
return false;
}
/** @type {?} */
const position = this.getItemPosition(item);
if (position.x === 0) {
return false;
}
/** @type {?} */
const rowBelowItem = this.grid[position.x - 1];
return (rowBelowItem || [])
.slice(position.y, position.y + position.h)
.reduce((isFloating, cellItem) => {
return isFloating && !cellItem;
}, true);
}
/**
* @param {?} item
* @param {?} options
* @return {?}
*/
isItemValidForGrid(item, options) {
/** @type {?} */
const 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 {?}
*/
findDefaultPositionHorizontal(width, height) {
for (const col of this.grid) {
/** @type {?} */
const colIdx = this.grid.indexOf(col);
/** @type {?} */
let rowIdx = 0;
while (rowIdx < col.length - height + 1) {
if (!this.checkItemsInArea(colIdx, colIdx + width - 1, rowIdx, rowIdx + height - 1)) {
return [colIdx, rowIdx];
}
rowIdx++;
}
}
return [this.grid.length, 0];
}
/**
* @param {?} width
* @param {?} height
* @return {?}
*/
findDefaultPositionVertical(width, height) {
for (const row of this.grid) {
/** @type {?} */
const rowIdx = this.grid.indexOf(row);
/** @type {?} */
let colIdx = 0;
while (colIdx < row.length - width + 1) {
if (!this.checkItemsInArea(rowIdx, rowIdx + height - 1, colIdx, colIdx + width - 1)) {
return [colIdx, rowIdx];
}
colIdx++;
}
}
return [0, this.grid.length];
}
/**
* @param {?} rowStart
* @param {?} rowEnd
* @param {?} colStart
* @param {?} colEnd
* @param {?=} item
* @return {?}
*/
checkItemsInArea(rowStart, rowEnd, colStart, colEnd, item) {
for (let i = rowStart; i <= rowEnd; i++) {
for (let 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 {?}
*/
sortItemsByPosition() {
this.items.sort((item1, item2) => {
/** @type {?} */
const position1 = this.getItemPosition(item1);
/** @type {?} */
const 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.
* @return {?}
*/
adjustSizeOfItems() {
for (let i = 0; i < this.items.length; i++) {
/** @type {?} */
const 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 {?}
*/
resetGrid() {
this.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 {?}
*/
itemFitsAtPosition(item, newPosition) {
/** @type {?} */
const position = this.getItemPosition(item);
/** @type {?} */
let x;
/** @type {?} */
let 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 {?} */
const 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 {?}
*/
updateItemPosition(item, position) {
if (item.x !== null && item.y !== null) {
this.deleteItemPositionFromGrid(item);
}
this.setItemPosition(item, position);
this.markItemPositionToGrid(item);
}
/**
* @param {?} item
* @param {?} width
* @param {?} height
* @return {?}
*/
updateItemSize(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
* @param {?} item
* @return {?}
*/
markItemPositionToGrid(item) {
/** @type {?} */
const position = this.getItemPosition(item);
/** @type {?} */
let x;
/** @type {?} */
let 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.
* @param {?} N
* @return {?}
*/
ensureColumns(N) {
for (let i = 0; i < N; i++) {
if (!this.grid[i]) {
this.grid.push(new GridCol(this.options.lanes));
}
}
}
/**
* @param {?} item
* @return {?}
*/
getItemsCollidingWithItem(item) {
/** @type {?} */
const collidingItems = [];
for (let 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 {?}
*/
itemsAreColliding(item1, item2) {
/** @type {?} */
const position1 = this.getItemPosition(item1);
/** @type {?} */
const 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
* @param {?} item
* @return {?}
*/
tryToResolveCollisionsLocally(item) {
/** @type {?} */
const collidingItems = this.getItemsCollidingWithItem(item);
if (!collidingItems.length) {
return true;
}
/** @type {?} */
const _gridList = new GridList(this.items.map(itm => {
return itm.copy();
}), this.options);
/** @type {?} */
let leftOfItem;
/** @type {?} */
let rightOfItem;
/** @type {?} */
let aboveOfItem;
/** @type {?} */
let belowOfItem;
for (let i = 0; i < collidingItems.length; i++) {
/** @type {?} */
const collidingItem = _gridList.items[collidingItems[i]];
/** @type {?} */
const 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 {?} */
const 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((itm, idx) => {
/** @type {?} */
const cachedItem = _gridList.items.filter(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
* @param {?} item
* @return {?}
*/
findLeftMostPositionForItem(item) {
/** @type {?} */
let tail = 0;
/** @type {?} */
const position = this.getItemPosition(item);
for (let i = 0; i < this.grid.length; i++) {
for (let j = position.y; j < position.y + position.h; j++) {
/** @type {?} */
const otherItem = this.grid[i][j];
if (!otherItem) {
continue;
}
/** @type {?} */
const 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 {?}
*/
findItemByPosition(x, y) {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].x === x && this.items[i].y === y) {
return this.items[i];
}
}
}
/**
* @param {?} key
* @param {?} value
* @return {?}
*/
getItemByAttribute(key, value) {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i][key] === value) {
return this.items[i];
}
}
return null;
}
/**
* @param {?} nr
* @param {?} prefix
* @return {?}
*/
padNumber(nr, prefix) {
// Currently works for 2-digit numbers (<100)
return nr >= 10 ? nr : prefix + nr;
}
/**
* If the direction is vertical we need to rotate the grid 90 deg to the
* left. Thus, we simulate the fact that items are being pulled to the top.
*
* Since the items have widths and heights, if we apply the classic
* counter-clockwise 90 deg rotation
*
* [0 -1]
* [1 0]
*
* then the top left point of an item will become the bottom left point of
* the rotated item. To adjust for this, we need to subtract from the y
* position the height of the original item - the width of the rotated item.
*
* However, if we do this then we'll reverse some actions: resizing the
* width of an item will stretch the item to the left instead of to the
* right; resizing an item that doesn't fit into the grid will push the
* items around it instead of going on a new row, etc.
*
* We found it better to do a vertical flip of the grid after rotating it.
* This restores the direction of the actions and greatly simplifies the
* transformations.
* @param {?} item
* @return {?}
*/
getItemPosition(item) {
if (this.options.direction === 'horizontal') {
return item;
}
else {
return {
x: item.y,
y: item.x,
w: item.h,
h: item.w
};
}
}
/**
* See getItemPosition.
* @param {?} item
* @param {?} position
* @return {?}
*/
setItemPosition(item, position) {
if (this.options.direction === 'horizontal') {
item.x = position[0];
item.y = position[1];
}
else {
// We're supposed to subtract the rotated item's height which is actually
// the non-rotated item's width.
item.x = position[1];
item.y = position[0];
}
}
}
if (false) {
/** @type {?} */
GridList.prototype.items;
/** @type {?} */
GridList.prototype.grid;
/** @type {?} */
GridList.prototype.options;
}
export { ɵ0 };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZExpc3QuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9AYmxhcmUvYW5ndWxhcjJncmlkc3Rlci8iLCJzb3VyY2VzIjpbImxpYi9ncmlkTGlzdC9ncmlkTGlzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztNQUdNLE9BQU8sR0FBRyxVQUFTLEtBQUs7SUFDMUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUM1QixJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQ25CO0FBQ0wsQ0FBQzs7O0FBRUQsT0FBTyxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBMkJ2QixNQUFNLE9BQU8sUUFBUTs7Ozs7SUFNakIsWUFBWSxLQUEwQixFQUFFLE9BQXlCO1FBQzdELElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBRXZCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBRW5CLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBRXpCLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN4QixDQUFDOzs7Ozs7Ozs7Ozs7OztJQWNELFFBQVE7O2NBQ0UsV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTTs7WUFDaEMsTUFBTSxHQUFHLE9BQU87O1lBQ2hCLE1BQU0sR0FBRyxPQUFPOztZQUNoQixJQUFJOztZQUNKLENBQUM7O1lBQ0QsQ0FBQztRQUVMLDBCQUEwQjtRQUMxQixLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFdBQVcsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUM5QixNQUFNLElBQUksR0FBRyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ3ZDLE1BQU0sSUFBSSxLQUFLLENBQUM7U0FDbkI7UUFDRCxNQUFNLElBQUks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