muuri
Version:
Responsive, sortable, filterable and draggable grid layouts.
359 lines (305 loc) • 8.06 kB
JavaScript
/**
* Copyright (c) 2015-present, Haltu Oy
* Released under the MIT license
* https://github.com/haltu/muuri/blob/master/LICENSE.md
*/
import { gridInstances } from '../shared.js';
import ItemAnimate from './ItemAnimate.js';
import ItemDrag from './ItemDrag.js';
import ItemLayout from './ItemLayout.js';
import ItemMigrate from './ItemMigrate.js';
import ItemRelease from './ItemRelease.js';
import ItemVisibility from './ItemVisibility.js';
import addClass from '../utils/addClass.js';
import createUid from '../utils/createUid.js';
import getStyle from '../utils/getStyle.js';
import getStyleAsFloat from '../utils/getStyleAsFloat.js';
import getTranslateString from '../utils/getTranslateString.js';
import removeClass from '../utils/removeClass.js';
import { transformProp } from '../utils/supportedTransform.js';
/**
* Creates a new Item instance for a Grid instance.
*
* @class
* @param {Grid} grid
* @param {HTMLElement} element
* @param {Boolean} [isActive]
*/
function Item(grid, element, isActive) {
var settings = grid._settings;
// Create instance id.
this._id = createUid();
// Reference to connected Grid instance's id.
this._gridId = grid._id;
// Destroyed flag.
this._isDestroyed = false;
// Set up initial positions.
this._left = 0;
this._top = 0;
// The elements.
this._element = element;
this._child = element.children[0];
// If the provided item element is not a direct child of the grid container
// element, append it to the grid container.
if (element.parentNode !== grid._element) {
grid._element.appendChild(element);
}
// Set item class.
addClass(element, settings.itemClass);
// If isActive is not defined, let's try to auto-detect it.
if (typeof isActive !== 'boolean') {
isActive = getStyle(element, 'display') !== 'none';
}
// Set up active state (defines if the item is considered part of the layout
// or not).
this._isActive = isActive;
// Set element's initial position styles.
element.style.left = '0';
element.style.top = '0';
element.style[transformProp] = getTranslateString(0, 0);
// Initiate item's animation controllers.
this._animate = new ItemAnimate(element);
this._animateChild = new ItemAnimate(this._child);
// Setup visibility handler.
this._visibility = new ItemVisibility(this);
// Set up layout handler.
this._layout = new ItemLayout(this);
// Set up migration handler data.
this._migrate = new ItemMigrate(this);
// Set up release handler
this._release = new ItemRelease(this);
// Set up drag handler.
this._drag = settings.dragEnabled ? new ItemDrag(this) : null;
// Set up the initial dimensions and sort data.
this._refreshDimensions();
this._refreshSortData();
}
/**
* Public prototype methods
* ************************
*/
/**
* Get the instance grid reference.
*
* @public
* @memberof Item.prototype
* @returns {Grid}
*/
Item.prototype.getGrid = function() {
return gridInstances[this._gridId];
};
/**
* Get the instance element.
*
* @public
* @memberof Item.prototype
* @returns {HTMLElement}
*/
Item.prototype.getElement = function() {
return this._element;
};
/**
* Get instance element's cached width.
*
* @public
* @memberof Item.prototype
* @returns {Number}
*/
Item.prototype.getWidth = function() {
return this._width;
};
/**
* Get instance element's cached height.
*
* @public
* @memberof Item.prototype
* @returns {Number}
*/
Item.prototype.getHeight = function() {
return this._height;
};
/**
* Get instance element's cached margins.
*
* @public
* @memberof Item.prototype
* @returns {Object}
* - The returned object contains left, right, top and bottom properties
* which indicate the item element's cached margins.
*/
Item.prototype.getMargin = function() {
return {
left: this._marginLeft,
right: this._marginRight,
top: this._marginTop,
bottom: this._marginBottom
};
};
/**
* Get instance element's cached position.
*
* @public
* @memberof Item.prototype
* @returns {Object}
* - The returned object contains left and top properties which indicate the
* item element's cached position in the grid.
*/
Item.prototype.getPosition = function() {
return {
left: this._left,
top: this._top
};
};
/**
* Is the item active?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isActive = function() {
return this._isActive;
};
/**
* Is the item visible?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isVisible = function() {
return !!this._visibility && !this._visibility._isHidden;
};
/**
* Is the item being animated to visible?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isShowing = function() {
return !!(this._visibility && this._visibility._isShowing);
};
/**
* Is the item being animated to hidden?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isHiding = function() {
return !!(this._visibility && this._visibility._isHiding);
};
/**
* Is the item positioning?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isPositioning = function() {
return !!(this._layout && this._layout._isActive);
};
/**
* Is the item being dragged?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isDragging = function() {
return !!(this._drag && this._drag._isActive);
};
/**
* Is the item being released?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isReleasing = function() {
return !!(this._release && this._release._isActive);
};
/**
* Is the item destroyed?
*
* @public
* @memberof Item.prototype
* @returns {Boolean}
*/
Item.prototype.isDestroyed = function() {
return this._isDestroyed;
};
/**
* Private prototype methods
* *************************
*/
/**
* Recalculate item's dimensions.
*
* @private
* @memberof Item.prototype
*/
Item.prototype._refreshDimensions = function() {
if (this._isDestroyed || this._visibility._isHidden) return;
var element = this._element;
var rect = element.getBoundingClientRect();
// Calculate width and height.
this._width = rect.width;
this._height = rect.height;
// Calculate margins (ignore negative margins).
this._marginLeft = Math.max(0, getStyleAsFloat(element, 'margin-left'));
this._marginRight = Math.max(0, getStyleAsFloat(element, 'margin-right'));
this._marginTop = Math.max(0, getStyleAsFloat(element, 'margin-top'));
this._marginBottom = Math.max(0, getStyleAsFloat(element, 'margin-bottom'));
};
/**
* Fetch and store item's sort data.
*
* @private
* @memberof Item.prototype
*/
Item.prototype._refreshSortData = function() {
if (this._isDestroyed) return;
var data = (this._sortData = {});
var getters = this.getGrid()._settings.sortData;
var prop;
for (prop in getters) {
data[prop] = getters[prop](this, this._element);
}
};
/**
* Destroy item instance.
*
* @private
* @memberof Item.prototype
* @param {Boolean} [removeElement=false]
*/
Item.prototype._destroy = function(removeElement) {
if (this._isDestroyed) return;
var element = this._element;
var grid = this.getGrid();
var settings = grid._settings;
var index = grid._items.indexOf(this);
// Destroy handlers.
this._release.destroy();
this._migrate.destroy();
this._layout.destroy();
this._visibility.destroy();
this._animate.destroy();
this._animateChild.destroy();
this._drag && this._drag.destroy();
// Remove all inline styles.
element.removeAttribute('style');
this._child.removeAttribute('style');
// Remove item class.
removeClass(element, settings.itemClass);
// Remove item from Grid instance if it still exists there.
index > -1 && grid._items.splice(index, 1);
// Remove element from DOM.
removeElement && element.parentNode.removeChild(element);
// Reset state.
this._isActive = false;
this._isDestroyed = true;
};
export default Item;