253 lines
11 KiB
JavaScript
253 lines
11 KiB
JavaScript
/**
|
|
* Stacking items in columns
|
|
* @module Ink.UI.Stacker_1
|
|
* @version 1
|
|
**/
|
|
|
|
Ink.createModule('Ink.UI.Stacker', 1, ['Ink.UI.Common_1', 'Ink.Dom.Event_1', 'Ink.Dom.Element_1'], function(Common, InkEvent, InkElement) {
|
|
'use strict';
|
|
|
|
function Stacker() {
|
|
Common.BaseUIComponent.apply(this, arguments);
|
|
}
|
|
|
|
Stacker._name = 'Stacker_1';
|
|
|
|
Stacker._optionDefinition = {
|
|
column: ['String', '.stacker-column'],
|
|
item: ['String', '.stacker-item'],
|
|
|
|
// [3.0.0] review this when we have info about our breakpoints from the CSS
|
|
customBreakPoints: ['Object', null], // Must be: {xlarge: {max: 9999, min: 1281, cols: 5}, large:{max:1280, min:1001, cols:4} medium:{max:1000, min:801,cols:3}, ...etc..}
|
|
largeMax: ['Number', Number.MAX_VALUE],
|
|
largeMin: ['Number', 961],
|
|
mediumMax: ['Number', 960],
|
|
mediumMin: ['Number', 651],
|
|
smallMax: ['Number', 650],
|
|
smallMin: ['Number', 0],
|
|
|
|
largeCols: ['Integer', 3],
|
|
mediumCols: ['Integer', 2],
|
|
smallCols: ['Integer', 1],
|
|
|
|
isOrdered: ['Boolean', true],
|
|
onRunCallback: ['Function', null],
|
|
onResizeCallback: ['Function', null],
|
|
onAPIReloadCallback: ['Function', null]
|
|
};
|
|
|
|
Stacker.prototype = {
|
|
/**
|
|
* This module combines several stacks of items together, in smaller screen sizes.
|
|
*
|
|
* The purpose is to have several stacks of items which may have different heights and as such cannot be used because of `float: left` quirks.
|
|
*
|
|
* For example, when you have three different columns of information:
|
|
*
|
|
* [col. A: 1] [col. B: 1] [col. C: 1]
|
|
* [col. B: 2] [col. C: 2] [col. C: 2]
|
|
*
|
|
* and the screen resizes and you need a layout of 2 columns, Stacker reorders the stacks so that you get:
|
|
*
|
|
* [col. A: 1] [col. B: 1]
|
|
* [col. C: 1] [col. A: 2]
|
|
* [col. B: 2] [col. C: 2]
|
|
*
|
|
* Note: If you just want to use a different amount of columns for your items in several viewports, but these items are guaranteed to have a fixed height, don't use this module. Use the `small-*`, `medium-*` and `large-*` classes instead.
|
|
*
|
|
* @class Ink.UI.Stacker_1
|
|
*
|
|
* @constructor
|
|
* @param {DOMElement|String} [container] Element which contains the stacks (identified by the options.column selector)
|
|
* @param {Object} [options] Options object.
|
|
* @param {String} [options.column] Selector for the the columns inside the container element. Defaults to '.stacker-column'.
|
|
* @param {String} [options.item] Selector for the items in your stack. Defaults to '.stacker-item'.
|
|
* @param {Object} [options.customBreakPoints] Options for each breakpoint name. Use this if you have more breakpoints than Ink by default (`large`, `medium`, `small`)
|
|
* @param {Object} [options.customBreakpoints.BREAKPOINT_NAME] Custom breakpoints object.
|
|
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.max Maximum screen size as seen in your media query
|
|
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.min Minimum screen size as seen in your media query
|
|
* @param {String} options.customBreakpoints.BREAKPOINT_NAME.cols Column count for this size.
|
|
* @param {Number} [options.largeMax] Upper bound of `large` breakpoint
|
|
* @param {Number} [options.largeMin] Lower bound of `large` breakpoint. Defaults to 961.
|
|
* @param {Number} [options.mediumMax] Upper bound of `medium` breakpoint. Defaults to 960.
|
|
* @param {Number} [options.mediumMin] Lower bound of `medium` breakpoint. Defaults to 651.
|
|
* @param {Number} [options.smallMax] Upper bound of `small` breakpoint. Defaults to 650.
|
|
* @param {Number} [options.smallMin] Lower bound of `small` breakpoint
|
|
*
|
|
* @param {Integer} [options.largeCols] Number of columns in the `large` viewport. Defaults to 3.
|
|
* @param {Integer} [options.mediumCols] Number of columns in the `medium` viewport. Defaults to 2.
|
|
* @param {Integer} [options.smallCols] Number of columns in the `small` viewport. Defaults to 1.
|
|
*
|
|
* @param {Boolean} [options.isOrdered] When false, doesn't reorder stacks when combining them.
|
|
* @param {Function} [options.onRunCallback] Called when instantiated.
|
|
* @param {Function} [options.onResizeCallback] Called when the window resizes.
|
|
* @param {Function} [options.onAPIReloadCallback] Called when the reload function executes.
|
|
*
|
|
* @sample Ink_UI_Stacker_1.html
|
|
**/
|
|
_init: function() {
|
|
this._aList = [];
|
|
|
|
this._curLayout = 'large';
|
|
|
|
// [todo] is this needed?
|
|
this._runFirstTime = false;
|
|
|
|
this._getPageItemsToList();
|
|
|
|
if(this._canApplyLayoutChange() || !this._runFirstTime) {
|
|
this._runFirstTime = true;
|
|
this._applyLayoutChange();
|
|
if(typeof(this._options.onRunCallback) === 'function') {
|
|
this._options.onRunCallback(this._curLayout);
|
|
}
|
|
}
|
|
this._addEvents();
|
|
},
|
|
|
|
/**
|
|
* Adds an item to the end of your stacks.
|
|
* Call `reloadItems()` when you are done adding items.
|
|
* @method addItem
|
|
* @param {DOMElement} item Element
|
|
**/
|
|
addItem: function(item) {
|
|
this._aList.push(item);
|
|
},
|
|
|
|
/**
|
|
* Updates the layout of your items.
|
|
* Call this method after adding items or changing their dimensions. This method is automatically called when the window resizes.
|
|
*
|
|
* @method reloadItems
|
|
**/
|
|
reloadItems: function() {
|
|
this._applyLayoutChange();
|
|
if(typeof(this._options.onAPIReloadCallback) === 'function') {
|
|
this._options.onAPIReloadCallback(this._curLayout);
|
|
}
|
|
},
|
|
|
|
_addEvents: function() {
|
|
InkEvent.observe(window, 'resize', Ink.bindEvent(this._onResize, this));
|
|
},
|
|
|
|
_onResize: function() {
|
|
if(this._canApplyLayoutChange()) {
|
|
this._removeDomItems();
|
|
this._applyLayoutChange();
|
|
if(typeof(this._options.onResizeCallback) === 'function') {
|
|
this._options.onResizeCallback(this._curLayout);
|
|
}
|
|
}
|
|
},
|
|
|
|
_setCurLayout: function() {
|
|
var viewportWidth = InkElement.viewportWidth();
|
|
if(this._options.customBreakpoints && typeof(this._options.customBreakPoints) === 'object') {
|
|
for(var prop in this._options.customBreakPoints) {
|
|
if(this._options.customBreakPoints.hasOwnProperty(prop)) {
|
|
if(viewportWidth >= Number(this._options.customBreakPoints[prop].min) && viewportWidth <= Number(this._options.customBreakPoints[prop].max) && this._curLayout !== prop) {
|
|
this._curLayout = prop;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if(viewportWidth <= Number(this._options.largeMax) && viewportWidth >= Number(this._options.largeMin) && this._curLayout !== 'large') {
|
|
this._curLayout = 'large';
|
|
} else if(viewportWidth >= Number(this._options.mediumMin) && viewportWidth <= Number(this._options.mediumMax) && this._curLayout !== 'medium') {
|
|
this._curLayout = 'medium';
|
|
} else if(viewportWidth >= Number(this._options.smallMin) && viewportWidth <= Number(this._options.smallMax) && this._curLayout !== 'small') {
|
|
this._curLayout = 'small';
|
|
}
|
|
}
|
|
},
|
|
|
|
_getColumnsToShow: function() {
|
|
if(this._options.customBreakPoints && typeof(this._options.customBreakPoints) === 'object') {
|
|
return Number(this._options.customBreakPoints[this._curLayout].cols);
|
|
} else {
|
|
return Number(this._options[this._curLayout+'Cols']);
|
|
}
|
|
},
|
|
|
|
_canApplyLayoutChange: function() {
|
|
var curLayout = this._curLayout;
|
|
this._setCurLayout();
|
|
if(curLayout !== this._curLayout) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
_getPageItemsToList: function() {
|
|
this._aColumn = Ink.ss(this._options.column, this._element);
|
|
var totalCols = this._aColumn.length;
|
|
var index = 0;
|
|
if(totalCols > 0) {
|
|
for(var i=0; i < this._aColumn.length; i++) {
|
|
var aItems = Ink.ss(this._options.item, this._aColumn[i]);
|
|
for(var j=0; j < aItems.length; j++) {
|
|
if(this._options.isOrdered) {
|
|
index = i + (j * totalCols);
|
|
}
|
|
this._aList[index] = aItems[j];
|
|
if(!this._options.isOrdered) {
|
|
index++;
|
|
}
|
|
//aItems[j].style.height = (100 + (Math.random() * 100))+'px';
|
|
aItems[j].parentNode.removeChild(aItems[j]);
|
|
}
|
|
}
|
|
if(this._aList.length > 0 && this._options.isOrdered) {
|
|
var aNewList = [];
|
|
for(var ii=0; ii < this._aList.length; ii++) {
|
|
if(typeof(this._aList[ii]) !== 'undefined') {
|
|
aNewList.push(this._aList[ii]);
|
|
}
|
|
}
|
|
this._aList = aNewList;
|
|
}
|
|
}
|
|
},
|
|
|
|
_removeDomItems: function() {
|
|
var totalCols = this._aColumn.length;
|
|
if(totalCols > 0) {
|
|
for(var i=0; i < totalCols; i++) {
|
|
var aItems = Ink.ss(this._options.item, this._aColumn[i]);
|
|
for(var j=aItems.length - 1; j >= 0; j--) {
|
|
aItems[j].parentNode.removeChild(aItems[j]);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
_applyLayoutChange: function() {
|
|
var totalCols = this._getColumnsToShow();
|
|
var totalItems = this._aList.length;
|
|
var index = 0;
|
|
var countCol = 0;
|
|
if(totalCols > 0) {
|
|
while(countCol < totalCols) {
|
|
this._aColumn[countCol].appendChild(this._aList[index]);
|
|
index++;
|
|
countCol++;
|
|
if(index === totalItems) {
|
|
return;
|
|
}
|
|
if(countCol === totalCols) {
|
|
countCol = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
Common.createUIComponent(Stacker);
|
|
|
|
return Stacker;
|
|
|
|
});
|