'use strict'; import Point from './point'; import arrayMax from './array-max'; import arrayMin from './array-min'; /** * Determine the location of the next item, based on its size. * @param {Object} itemSize Object with width and height. * @param {Array.} positions Positions of the other current items. * @param {number} gridSize The column width or row height. * @param {number} total The total number of columns or rows. * @param {number} threshold Buffer value for the column to fit. * @param {number} buffer Vertical buffer for the height of items. * @return {Point} */ export function getItemPosition({ itemSize, positions, gridSize, total, threshold, buffer }) { var span = getColumnSpan(itemSize.width, gridSize, total, threshold); var setY = getAvailablePositions(positions, span, total); var shortColumnIndex = getShortColumn(setY, buffer); // Position the item var point = new Point( Math.round(gridSize * shortColumnIndex), Math.round(setY[shortColumnIndex])); // Update the columns array with the new values for each column. // e.g. before the update the columns could be [250, 0, 0, 0] for an item // which spans 2 columns. After it would be [250, itemHeight, itemHeight, 0]. var setHeight = setY[shortColumnIndex] + itemSize.height; for (var i = 0; i < span; i++) { positions[shortColumnIndex + i] = setHeight; } return point; } /** * Determine the number of columns an items spans. * @param {number} itemWidth Width of the item. * @param {number} columnWidth Width of the column (includes gutter). * @param {number} columns Total number of columns * @param {number} threshold A buffer value for the size of the column to fit. * @return {number} */ export function getColumnSpan(itemWidth, columnWidth, columns, threshold) { var columnSpan = itemWidth / columnWidth; // If the difference between the rounded column span number and the // calculated column span number is really small, round the number to // make it fit. if (Math.abs(Math.round(columnSpan) - columnSpan) < threshold) { // e.g. columnSpan = 4.0089945390298745 columnSpan = Math.round(columnSpan); } // Ensure the column span is not more than the amount of columns in the whole layout. return Math.min(Math.ceil(columnSpan), columns); } /** * Retrieves the column set to use for placement. * @param {number} columnSpan The number of columns this current item spans. * @param {number} columns The total columns in the grid. * @return {Array.} An array of numbers represeting the column set. */ export function getAvailablePositions(positions, columnSpan, columns) { // The item spans only one column. if (columnSpan === 1) { return positions; } // The item spans more than one column, figure out how many different // places it could fit horizontally. // The group count is the number of places within the positions this block // could fit, ignoring the current positions of items. // Imagine a 2 column brick as the second item in a 4 column grid with // 10px height each. Find the places it would fit: // [20, 10, 10, 0] // | | | // * * * // // Then take the places which fit and get the bigger of the two: // max([20, 10]), max([10, 10]), max([10, 0]) = [20, 10, 0] // // Next, find the first smallest number (the short column). // [20, 10, 0] // | // * // // And that's where it should be placed! // // Another example where the second column's item extends past the first: // [10, 20, 10, 0] => [20, 20, 10] => 10 var available = []; // For how many possible positions for this item there are. for (var i = 0; i <= columns - columnSpan; i++) { // Find the bigger value for each place it could fit. available.push(arrayMax(positions.slice(i, i + columnSpan))); } return available; } /** * Find index of short column, the first from the left where this item will go. * * @param {Array.} positions The array to search for the smallest number. * @param {number} buffer Optional buffer which is very useful when the height * is a percentage of the width. * @return {number} Index of the short column. */ export function getShortColumn(positions, buffer) { var minPosition = arrayMin(positions); for (var i = 0, len = positions.length; i < len; i++) { if (positions[i] >= minPosition - buffer && positions[i] <= minPosition + buffer) { return i; } } return 0; }