packery
Version:
bin-packing layout library
185 lines (155 loc) • 4.94 kB
JavaScript
/**
* Packery Item Element
**/
( function( window ) {
'use strict';
// -------------------------- Item -------------------------- //
function itemDefinition( getStyleProperty, Outlayer, Rect ) {
var transformProperty = getStyleProperty('transform');
// sub-class Item
var Item = function PackeryItem() {
Outlayer.Item.apply( this, arguments );
};
Item.prototype = new Outlayer.Item();
var protoCreate = Item.prototype._create;
Item.prototype._create = function() {
// call default _create logic
protoCreate.call( this );
this.rect = new Rect();
// rect used for placing, in drag or Packery.fit()
this.placeRect = new Rect();
};
// -------------------------- drag -------------------------- //
Item.prototype.dragStart = function() {
this.getPosition();
this.removeTransitionStyles();
// remove transform property from transition
if ( this.isTransitioning && transformProperty ) {
this.element.style[ transformProperty ] = 'none';
}
this.getSize();
// create place rect, used for position when dragged then dropped
// or when positioning
this.isPlacing = true;
this.needsPositioning = false;
this.positionPlaceRect( this.position.x, this.position.y );
this.isTransitioning = false;
this.didDrag = false;
};
/**
* handle item when it is dragged
* @param {Number} x - horizontal position of dragged item
* @param {Number} y - vertical position of dragged item
*/
Item.prototype.dragMove = function( x, y ) {
this.didDrag = true;
var packerySize = this.layout.size;
x -= packerySize.paddingLeft;
y -= packerySize.paddingTop;
this.positionPlaceRect( x, y );
};
Item.prototype.dragStop = function() {
this.getPosition();
var isDiffX = this.position.x !== this.placeRect.x;
var isDiffY = this.position.y !== this.placeRect.y;
// set post-drag positioning flag
this.needsPositioning = isDiffX || isDiffY;
// reset flag
this.didDrag = false;
};
// -------------------------- placing -------------------------- //
/**
* position a rect that will occupy space in the packer
* @param {Number} x
* @param {Number} y
* @param {Boolean} isMaxYContained
*/
Item.prototype.positionPlaceRect = function( x, y, isMaxYOpen ) {
this.placeRect.x = this.getPlaceRectCoord( x, true );
this.placeRect.y = this.getPlaceRectCoord( y, false, isMaxYOpen );
};
/**
* get x/y coordinate for place rect
* @param {Number} coord - x or y
* @param {Boolean} isX
* @param {Boolean} isMaxOpen - does not limit value to outer bound
* @returns {Number} coord - processed x or y
*/
Item.prototype.getPlaceRectCoord = function( coord, isX, isMaxOpen ) {
var measure = isX ? 'Width' : 'Height';
var size = this.size[ 'outer' + measure ];
var segment = this.layout[ isX ? 'columnWidth' : 'rowHeight' ];
var parentSize = this.layout.size[ 'inner' + measure ];
// additional parentSize calculations for Y
if ( !isX ) {
parentSize = Math.max( parentSize, this.layout.maxY );
// prevent gutter from bumping up height when non-vertical grid
if ( !this.layout.rowHeight ) {
parentSize -= this.layout.gutter;
}
}
var max;
if ( segment ) {
segment += this.layout.gutter;
// allow for last column to reach the edge
parentSize += isX ? this.layout.gutter : 0;
// snap to closest segment
coord = Math.round( coord / segment );
// contain to outer bound
// contain non-growing bound, allow growing bound to grow
var mathMethod;
if ( this.layout.options.isHorizontal ) {
mathMethod = !isX ? 'floor' : 'ceil';
} else {
mathMethod = isX ? 'floor' : 'ceil';
}
var maxSegments = Math[ mathMethod ]( parentSize / segment );
maxSegments -= Math.ceil( size / segment );
max = maxSegments;
} else {
max = parentSize - size;
}
coord = isMaxOpen ? coord : Math.min( coord, max );
coord *= segment || 1;
return Math.max( 0, coord );
};
Item.prototype.copyPlaceRectPosition = function() {
this.rect.x = this.placeRect.x;
this.rect.y = this.placeRect.y;
};
// ----- ----- //
// remove element from DOM
Item.prototype.removeElem = function() {
this.element.parentNode.removeChild( this.element );
// add space back to packer
this.layout.packer.addSpace( this.rect );
this.emitEvent( 'remove', [ this ] );
};
// ----- ----- //
return Item;
}
// -------------------------- transport -------------------------- //
if ( typeof define === 'function' && define.amd ) {
// AMD
define( [
'get-style-property/get-style-property',
'outlayer/outlayer',
'./rect'
],
itemDefinition );
} else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = itemDefinition(
require('desandro-get-style-property'),
require('outlayer'),
require('./rect')
);
} else {
// browser global
window.Packery.Item = itemDefinition(
window.getStyleProperty,
window.Outlayer,
window.Packery.Rect
);
}
})( window );