Docs updates. Remove LOADING and DONE events.

pull/111/head
Glen Cheney 8 years ago
parent eec4bfc157
commit 60cc0241b3

@ -13,7 +13,6 @@ exclude: [
'bower.json',
'CONTRIBUTING.md',
'gulpfile.js',
'package.json',
'README.md',
'webpack.config.js',
'webpack.config.min.js'

@ -1,27 +1,20 @@
<h2>Events<a href="#events"></a></h2>
<p>A list of events shuffle triggers:</p>
<ul class="breathable-list">
<li><code class="language-javascript">'loading.shuffle'</code></li>
<li><code class="language-javascript">'done.shuffle'</code></li>
<li><code class="language-javascript">'layout.shuffle'</code></li>
<li><code class="language-javascript">'removed.shuffle'</code></li>
</ul>
<p>Shuffle emits an event when a layout happens and when elements are removed. The event names are <code class="language-javascript">Shuffle.EventType.LAYOUT</code> and <code class="language-javascript">Shuffle.EventType.REMOVED</code>.</p>
<p>Shuffle uses the global <code>CustomEvent</code> to <a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent">create events</a>. A polyfill for IE&lt;=11 is bundled with Shuffle.</p>
<h3>Get notified when shuffle is done with setup</h3>
<h3>Get notified when a layout happens</h3>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">$grid.on('done.shuffle', function() {
console.log('Finished initializing shuffle!');
});
// Initialize shuffle
$grid.shuffle( options );</code></pre>
<pre rel="JavaScript"><code class="language-javascript">element.addEventListener(Shuffle.EventType.LAYOUT, function () {
console.log('Things finished moving!');
});</code></pre>
</div>
<h3>Do something when an item is removed</h3>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">$grid.on('removed.shuffle', function( evt, $collection, shuffle ) {
console.log( this, evt, $collection, shuffle );
<pre rel="JavaScript"><code class="language-javascript">element.addEventListener(Shuffle.EventType.REMOVED, function (evt) {
var detail = evt.detail;
console.log(this, evt, detail.collection, detail.shuffle);
});</code></pre>
</div>

@ -1,8 +1,7 @@
<h2>Features<a href="#features"></a></h2>
<ul>
<li>Fast - Only one forced synchronous layout (aka reflow) on init, sort, or shuffle.</li>
<li>Uses CSS Transitions!</li>
<li>Fast - Only one forced synchronous layout (aka reflow) on init, sort, or filter.</li>
<li>Responsive (try resizing the window!)</li>
<li>Filter items by groups</li>
<li>Items can have multiple groups and be different sizes</li>

@ -25,6 +25,8 @@
<link id="favicon" rel="icon" type="image/png" href="favicon.png" >
<script>window.location.host.indexOf("github.io")>-1&&"https:"!=window.location.protocol&&(window.location.protocol="https:");</script>
<!-- Prefetch DNS for external assets -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://themes.googleusercontent.com">

@ -4,22 +4,23 @@
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">// Overrideable options
Shuffle.options = {
group: ALL_ITEMS, // Initial filter group.
group: Shuffle.ALL_ITEMS, // Initial filter group.
speed: 250, // Transition/animation speed (milliseconds).
easing: 'ease-out', // CSS easing function to use.
itemSelector: '', // e.g. '.picture-item'.
sizer: null, // Sizer element. Use an element to determine the size of columns and gutters.
easing: 'ease', // CSS easing function to use.
itemSelector: '*', // e.g. '.picture-item'.
sizer: null, // Element or selector string. Use an element to determine the size of columns and gutters.
gutterWidth: 0, // A static number or function that tells the plugin how wide the gutters between columns are (in pixels).
columnWidth: 0, // A static number or function that returns a number which tells the plugin how wide the columns are (in pixels).
delimeter: null, // If your group is not json, and is comma delimeted, you could set delimeter to ','.
buffer: 0, // Useful for percentage based heights when they might not always be exactly the same (in pixels).
columnThreshold: HAS_COMPUTED_STYLE ? 0.01 : 0.1, // Reading the width of elements isn't precise enough and can cause columns to jump between values.
columnThreshold: 0.01, // Reading the width of elements isn't precise enough and can cause columns to jump between values.
initialSort: null, // Shuffle can be initialized with a sort object. It is the same object given to the sort method.
throttle: throttle, // By default, shuffle will throttle resize events. This can be changed or removed.
throttleTime: 300, // How often shuffle can be called on resize (in milliseconds).
sequentialFadeDelay: 150, // Delay between each item that fades in when adding items.
supported: CAN_TRANSITION_TRANSFORMS // Whether to use transforms or absolute positioning.
staggerAmount: 15, // Transition delay offset for each item in milliseconds.
staggerAmountMax: 250, // Maximum stagger delay in milliseconds.
useTransforms: true, // Whether to use transforms or absolute positioning.
};</code></pre>
</div>
<p>No options <em>need</em> to be specified, but <code>itemSelector</code> should be used. Other common options to change are <code>speed</code>, <code>easing</code>, <code>gutterWidth</code>, and <code>columnWidth</code> (or <code>sizer</code>).</p>
<p>No options <em>need</em> to be specified, but <code>itemSelector</code> should be used. Other common options to change are <code>speed</code> and <code>sizer</code>.</p>

@ -3,47 +3,45 @@
<h3>The HTML Structure</h3>
<p>The only real important thing here is the <code class="language-markup token attr-name">data-groups</code> attribute. It has to be a <a href="http://jsonlint.com/">valid JSON</a> array of strings. Optionally, it can be a string delimeted by a value you provide. See <code>delimeter</code> in the <a href="#options">options</a>.</p>
<p>In this example, shuffle is using the fluid grid from the <a href="http://twitter.github.io/bootstrap/">Twitter Bootstrap v2.3</a>. It's also making use of <a href="http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/"><abbr title="Block-Element-Modifier">BEM</abbr></a> class naming.</p>
<p>This example is using this site's grid. Each item would be 4 columns at the "sm" breakpoint (768px).</p>
<h3>Images</h3>
<p>To see why the images are wrapped in <code>.aspect</code> elements, check out the <a href="{{ site.baseurl }}{% post_url 2013-05-03-images %}">images demo</a>.</p>
<div class="code-block">
<pre rel="HTML"><code class="language-markup">&lt;div id="grid" class="row"&gt;
&lt;figure class="col-3@sm picture-item" data-groups='["photography"]'&gt;
&lt;img src="/img/baseball.png" height="145" width="230" /&gt;
&lt;div class="picture-item__details"&gt;
&lt;figcaption class="picture-item__title"&gt;Baseball&lt;/figcaption&gt;
&lt;p class="picture-item__tags"&gt;photography&lt;/p&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class="col-6@sm picture-item" data-groups='["wallpaper","3d"]'&gt;
&lt;img src="/img/tennis-ball.png" height="145" width="230" /&gt;
&lt;div class="picture-item__details"&gt;
&lt;figcaption class="picture-item__title"&gt;Tennis&lt;/figcaption&gt;
&lt;p class="picture-item__tags"&gt;wallpaper, 3d&lt;/p&gt;
<pre rel="HTML"><code class="language-markup">&lt;div id="grid" class="row my-shuffle-container"&gt;
&lt;figure class="col-4@sm picture-item" data-groups='[&quot;photography&quot;]' data-date-created="2010-09-14" data-title="Baseball"&gt;
&lt;div class="aspect aspect--16x9"&gt;
&lt;div class="aspect__inner"&gt;
&lt;img src="/img/baseball.png" alt="" height="145" width="230"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="picture-item__description"&gt;Some description here.&lt;/p&gt;
&lt;figcaption&gt;Baseball&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="col-3@sm picture-item" data-groups='["wallpaper","3d"]'&gt;
&lt;img src="/img/imac.png" height="145" width="230" /&gt;
&lt;div class="picture-item__details"&gt;
&lt;figcaption class="picture-item__title"&gt;iMac&lt;/figcaption&gt;
&lt;p class="picture-item__tags"&gt;wallpaper, 3d&lt;/p&gt;
&lt;figure class="col-4@sm picture-item" data-groups='[&quot;wallpaper&quot;,&quot;3d&quot;]' data-date-created="2011-08-14" data-title="Tennis"&gt;
&lt;div class="aspect aspect--16x9"&gt;
&lt;div class="aspect__inner"&gt;
&lt;img src="/img/tennis-ball.png" alt="" height="145" width="230"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figcaption&gt;Tennis&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="col-3@sm picture-item picture-item--h2" data-groups='["graphics"]'&gt;
&lt;img src="/img/master-chief.png" height="145" width="230" /&gt;
&lt;div class="picture-item__details"&gt;
&lt;figcaption class="picture-item__title"&gt;Master Chief&lt;/figcaption&gt;
&lt;p class="picture-item__tags"&gt;graphics&lt;/p&gt;
&lt;figure class="col-4@sm picture-item" data-groups='[&quot;wallpaper&quot;,&quot;3d&quot;]' data-date-created="2009-05-27" data-title="iMac"&gt;
&lt;div class="aspect aspect--16x9"&gt;
&lt;div class="aspect__inner"&gt;
&lt;img src="/img/imac.png" alt="" height="145" width="230"&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p class="picture-item__description"&gt;Some description here.&lt;/p&gt;
&lt;figcaption&gt;iMac&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;div class="col-1@sm my-sizer-element"&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
</div>
<h3 id="columns">How column widths work<a href="#how-column-widths-work"></a></h3>
<p>The <code>columnWidth</code> option is used to calculate the column width. You have several options:</p>
<p>There are 4 options for defining the width of the columns:</p>
<ol>
<li>Use a <strong>sizer</strong> element. This is the easest way to specify column and gutter widths. You can use an element or an element wrapped in jQuery to define the column width and gutter width. Shuffle will measure the <code>width</code> and <code>margin-left</code> of this <code>sizer</code> element each time the grid resizes. This is awesome for responsive or fluid grids where the width of a column is a percentage. The <code>sizer</code> option is an alias for <code>columnWidth</code>.<span class="demo-link-container">See <a href="{{ site.baseurl }}{% post_url 2013-05-01-basic %}">a demo</a> using a sizer element or <a href="{{ site.baseurl }}/js/demos/homepage.js">look at the js file</a> for the sizer demo.</span></li>
<li>Use a <strong>sizer</strong> element. This is the easiest way to specify column and gutter widths. Add the sizer element and make it 1 column wide. Shuffle will measure the <code>width</code> and <code>margin-left</code> of this <code>sizer</code> element each time the grid resizes. This is awesome for responsive or fluid grids where the width of a column is a percentage.</li>
<li>Use a <strong>function</strong>. When a function is used, its first parameter will be the width of the shuffle element. You need to return the column width for shuffle to use (in pixels).</li>
<li>A <strong>number</strong>. This will explicitly set the column width to your number (in pixels).</li>
<li>By default, shuffle will use the width of the first item to calculate the column width.</li>
@ -51,14 +49,14 @@
<h3>A basic setup example</h3>
<p>If you want functional buttons, check out <a href="{{ site.baseurl }}/js/demos/homepage.js">the js file</a>.</p>
<p>Shuffle uses a UMD definition so that you can use it with globals, AMD, or CommonJS.</p>
<div class="code-block">
<pre rel="JavaScript"><code class="language-javascript">$(document).ready(function() {
var $grid = $('#grid'),
$sizer = $grid.find('.shuffle__sizer');
<pre rel="JavaScript"><code class="language-javascript">var Shuffle = window.shuffle;
var element = document.getElementById('grid');
var sizer = element.querySelector('.my-sizer-element');
$grid.shuffle({
itemSelector: '.picture-item',
sizer: $sizer
});
var shuffle = new Shuffle(element, {
itemSelector: '.picture-item',
sizer: sizer // could also be a selector: '.my-sizer-element'
});</code></pre>
</div>

@ -14,12 +14,12 @@ extraJS: [ "demos/homepage.js" ]
<div class="container">
<div id="grid" class="row shuffle--container shuffle--fluid">
<div id="grid" class="row my-shuffle-container">
{% for item in site.items %}
{% assign item = item %}
{% include picture-item.html %}
{% endfor %}
<div class="col-1@sm col-1@xs shuffle__sizer"></div>
<div class="col-1@sm col-1@xs my-sizer-element"></div>
</div>
</div>

@ -8,13 +8,17 @@ prism: true
---
<div class="container">
<h2>Using images with Shuffle</h2>
<p>You can encounter problems when shuffle item dimensions depend on images. <a href="{{ site.baseurl }}{% post_url 2013-06-29-image-problems %}">Like this demo</a>. There are three good solutions to this.</p>
<ol>
<li>Set an explicit height on <code>.shuffle-item</code>s like the <a href="{{ site.baseurl }}{% post_url 2013-05-01-basic %}">basic demo</a>.</li>
<li>Similar to number 1, make the height of the image container a percentage of the width. If you know the aspect ratio of the images you're using, this is the technique you should use. This demo uses this technique.</li>
<li>Get notified when images load and call <code>myShuffleInstance.layout()</code>. I recommend using <a href="http://desandro.github.io/imagesloaded/">Desandro's images loaded plugin</a> to know when your images have finished loading.</li>
</ol>
<div class="row">
<div class="col-12@sm">
<h2>Using images with Shuffle</h2>
<p>You can encounter problems when shuffle item dimensions depend on images. <a href="{{ site.baseurl }}{% post_url 2013-06-29-image-problems %}">Like this demo</a>. There are three good solutions to this.</p>
<ol>
<li>Set an explicit height on <code>.shuffle-item</code>s like the <a href="{{ site.baseurl }}{% post_url 2013-05-01-basic %}">basic demo</a>.</li>
<li>Similar to number 1, make the height of the image container a percentage of the width. If you know the aspect ratio of the images you're using, this is the technique you should use. This demo uses this technique.</li>
<li>Get notified when images load and call <code>myShuffleInstance.layout()</code>. I recommend using <a href="http://desandro.github.io/imagesloaded/">Desandro's images loaded plugin</a> to know when your images have finished loading.</li>
</ol>
</div>
</div>
</div>

@ -6,9 +6,13 @@ extraJS: [ "demos/images.js"]
---
<div class="container">
<h2>This probably looks broken.</h2>
<p>In this demo, the height of each item in the grid depends on the image. If Shuffle is initialized before the images load, the heights it calculates will be incorrect. <a href="{{ site.baseurl }}{% post_url 2013-05-03-images %}">See here</a> for a solution.</p>
<p>Resize the window and it'll fix itself.</p>
<div class="row">
<div class="col-12@sm">
<h2>This probably looks broken.</h2>
<p>In this demo, the height of each item in the grid depends on the image. If Shuffle is initialized before the images load, the heights it calculates will be incorrect. <a href="{{ site.baseurl }}{% post_url 2013-05-03-images %}">See here</a> for a solution.</p>
<p>Resize the window and it'll fix itself.</p>
</div>
</div>
</div>

@ -14,7 +14,7 @@ extraJS: [ "viewport.js", "demos/animate-in.js" ]
<div class="container">
<div id="grid" class="row shuffle--container shuffle--fluid shuffle--animatein">
<div id="grid" class="row my-shuffle-container shuffle--animatein">
{% for item in site.items %}
{% assign item = item %}
{% include picture-item.html %}
@ -27,7 +27,7 @@ extraJS: [ "viewport.js", "demos/animate-in.js" ]
{% assign item = item %}
{% include picture-item.html %}
{% endfor %}
<div class="col-1@sm col-1@xs shuffle__sizer"></div>
<div class="col-1@sm col-1@xs my-sizer-element"></div>
</div>
</div>

@ -63,7 +63,7 @@ extraJS: [ "demos/padding-grid.js" ]
<div class="col-xs-12 col-sm-9">
<div class="row">
<div id="grid" class="shuffle--container shuffle--fluid">
<div id="grid" class="my-shuffle-container">
{% for i in (1..20) %}
<div class="grid__brick col-xs-6 col-sm-4">
<div class="grid__brick-inner">
@ -75,7 +75,7 @@ extraJS: [ "demos/padding-grid.js" ]
</div>
</div>
{% endfor %}
<div class="col-xs-1 shuffle__sizer"></div>
<div class="col-xs-1 my-sizer-element"></div>
</div>
</div>

@ -11,10 +11,14 @@ pagejs: false
---
<div class="container">
<h2>RequireJS!</h2>
<p>
Shuffle uses a UMD wrapper, so it is compatible with AMD loaders like <a href="http://requirejs.org">RequireJS</a>. The UMD wrapper also allows Shuffle to work with CommonJS modules. You can take a peek at the <a href="{{ site.baseurl }}/js/require-main.js">config file</a> used on this page.
</p>
<div class="row">
<div class="col-12@sm">
<h2>RequireJS!</h2>
<p>
Shuffle uses a UMD wrapper, so it is compatible with AMD loaders like <a href="http://requirejs.org">RequireJS</a>. The UMD wrapper also allows Shuffle to work with CommonJS modules. You can take a peek at the <a href="{{ site.baseurl }}/js/require-main.js">config file</a> used on this page.
</p>
</div>
</div>
</div>
@ -34,7 +38,7 @@ pagejs: false
</style>
<div class="container shuffle-wrap">
<div id="grid" class="shuffle--container row">
<div id="grid" class="my-shuffle-container row">
{% for item in site.items %}
<figure class="js-item img-item col-3@sm col-3@xs">
<div class="aspect aspect--16x9">
@ -50,10 +54,12 @@ pagejs: false
</div>
<section>
<div class="container">
<h2>Shuffle.js</h2>
<p>{{ site.longDescription }}</p>
<section class="container">
<div class="row">
<div class="col-12@sm">
<h2>Shuffle.js</h2>
<p>{{ site.longDescription }}</p>
</div>
</div>
</section>

@ -87,8 +87,12 @@
pre {
position: relative;
z-index: 1;
min-height: 56px;
padding-top: 1em;
padding-bottom: 1em;
padding-left: calc(3.5vw + #{($grid-gutter-width / 2)});
padding-right: calc(3.5vw + #{($grid-gutter-width / 2)});
margin: .5em 0;
}
}

@ -108,12 +108,12 @@ $itemHeight: 220px;
It will set it for you, but it'll cause another style recalculation and layout.
AKA worse performance - so just set it here
*/
.shuffle--container {
.my-shuffle-container {
position: relative;
overflow: hidden;
}
.shuffle--fluid .shuffle__sizer {
.my-sizer-element {
position: absolute;
opacity: 0;
visibility: hidden;

@ -76,11 +76,11 @@
It will set it for you, but it'll cause another style recalculation and layout.
AKA worse performance - so just set it here
*/
.shuffle--container {
.my-shuffle-container {
position: relative;
overflow: hidden; }
.shuffle--fluid .shuffle__sizer {
.my-sizer-element {
position: absolute;
opacity: 0;
visibility: hidden; }

@ -997,8 +997,12 @@ h3:hover > a {
.code-block pre {
position: relative;
z-index: 1;
min-height: 56px;
padding-top: 1em;
padding-bottom: 1em;
padding-left: calc(3.5vw + 8px);
padding-right: calc(3.5vw + 8px); }
padding-right: calc(3.5vw + 8px);
margin: .5em 0; }
@media screen and (min-width: 768px) {
.code-block {

58
dist/shuffle.js vendored

@ -114,10 +114,6 @@ return /******/ (function(modules) { // webpackBootstrap
}
}
function defer(fn, context, wait) {
return setTimeout(fn.bind(context), wait);
}
function arrayMax(array) {
return Math.max.apply(Math, array);
}
@ -163,7 +159,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.isDestroyed = false;
this.isInitialized = false;
this._transitions = [];
this._isMovementCanceled = false;
this.isTransitioning = false;
this._queue = [];
element = this._getElementOption(element);
@ -175,15 +171,8 @@ return /******/ (function(modules) { // webpackBootstrap
this.element = element;
this.id = 'shuffle_' + id++;
this._dispatch(Shuffle.EventType.LOADING);
this._init();
// Dispatch the done event asynchronously so that people can bind to it after
// Shuffle has been initialized.
defer(function () {
this.isInitialized = true;
this._dispatch(Shuffle.EventType.DONE);
}, this, 16);
this.isInitialized = true;
}
_createClass(Shuffle, [{
@ -221,12 +210,13 @@ return /******/ (function(modules) { // webpackBootstrap
// Kick off!
this.filter(this.options.group, this.options.initialSort);
// The shuffle items haven't had transitions set on them yet
// so the user doesn't see the first layout. Set them now that the first layout is done.
defer(function () {
this._setTransitions();
this.element.style.transition = 'height ' + this.options.speed + 'ms ' + this.options.easing;
}, this);
// The shuffle items haven't had transitions set on them yet so the user
// doesn't see the first layout. Set them now that the first layout is done.
// First, however, a synchronous layout must be caused for the previous
// styles to be applied without transitions.
this.element.offsetWidth; // jshint ignore: line
this._setTransitions();
this.element.style.transition = 'height ' + this.options.speed + 'ms ' + this.options.easing;
}
/**
@ -535,16 +525,16 @@ return /******/ (function(modules) { // webpackBootstrap
var size;
// If the columnWidth property is a function, then the grid is fluid
if (typeof this.columnWidth === 'function') {
size = this.columnWidth(containerWidth);
if (typeof this.options.columnWidth === 'function') {
size = this.options.columnWidth(containerWidth);
// columnWidth option isn't a function, are they using a sizing element?
} else if (this.useSizer) {
size = Shuffle.getSize(this.options.sizer).width;
// if not, how about the explicitly set option?
} else if (this.columnWidth) {
size = this.columnWidth;
} else if (this.options.columnWidth) {
size = this.options.columnWidth;
// or use the size of the first item
} else if (this.items.length > 0) {
@ -654,6 +644,10 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _dispatch(name) {
var details = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
if (this.isDestroyed) {
return;
}
details.shuffle = this;
return !this.element.dispatchEvent(new CustomEvent(name, {
bubbles: true,
@ -957,13 +951,12 @@ return /******/ (function(modules) { // webpackBootstrap
key: '_transition',
value: function _transition(opts) {
opts.item.applyCss(this._getStylesForTransition(opts));
this._whenTransitionDone(opts.item.element, opts.callback);
return this._whenTransitionDone(opts.item.element, opts.callback);
}
/**
* Execute the styles gathered in the style queue. This applies styles to elements,
* triggering transitions.
* @param {boolean} withLayout Whether to trigger a layout event.
* @private
*/
@ -972,8 +965,6 @@ return /******/ (function(modules) { // webpackBootstrap
value: function _processQueue() {
var _this6 = this;
var withLayout = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
if (this.isTransitioning) {
this._cancelMovement();
}
@ -996,8 +987,8 @@ return /******/ (function(modules) { // webpackBootstrap
// A call to layout happened, but none of the newly filtered items will
// change position. Asynchronously fire the callback here.
} else if (withLayout) {
defer(this._dispatchLayout, this);
} else {
setTimeout(this._dispatchLayout.bind(this), 0);
}
// Remove everything in the style queue
@ -1421,8 +1412,6 @@ return /******/ (function(modules) { // webpackBootstrap
* @enum {string}
*/
Shuffle.EventType = {
LOADING: 'shuffle:loading',
DONE: 'shuffle:done',
LAYOUT: 'shuffle:layout',
REMOVED: 'shuffle:removed'
};
@ -1444,7 +1433,8 @@ return /******/ (function(modules) { // webpackBootstrap
// e.g. '.picture-item'.
itemSelector: '*',
// Sizer element. Use an element to determine the size of columns and gutters.
// Element or selector string. Use an element to determine the size of columns
// and gutters.
sizer: null,
// A static number or function that tells the plugin how wide the gutters
@ -1481,7 +1471,7 @@ return /******/ (function(modules) { // webpackBootstrap
// Transition delay offset for each item in milliseconds.
staggerAmount: 15,
// It can look a little weird when the last element is in the top row
// Maximum stagger delay in milliseconds.
staggerAmountMax: 250,
// Whether to use transforms or absolute positioning.
@ -2096,7 +2086,7 @@ return /******/ (function(modules) { // webpackBootstrap
function cancelTransitionEnd(id) {
if (transitions[id]) {
transitions[id].element.removeEventListener(eventName, transitions[id].listener);
delete transitions[id];
transitions[id] = null;
return true;
}

File diff suppressed because one or more lines are too long

@ -70,12 +70,12 @@ prism: true
</div>
<div class="container">
<div id="grid" class="row shuffle--container shuffle--fluid">
<div id="grid" class="row my-shuffle-container">
{% for item in site.items %}
{% assign item = item %}
{% include picture-item.html %}
{% endfor %}
<div class="col-1@sm col-1@xs shuffle__sizer"></div>
<div class="col-1@sm col-1@xs my-sizer-element"></div>
</div>
</div>
</section>
@ -188,6 +188,8 @@ prism: true
<div class="row">
<div class="col-12@sm">
<h2>Dependencies<a href="#dependencies"></a></h2>
<p>Shuffle assumes <code>Promise</code> is available globally. If you care about IE11, use a <a href="https://github.com/stefanpenner/es6-promise">polyfill</a>. <a href="http://caniuse.com/#feat=promises">Current support</a>.</p>
<p>Shuffle's <a href="{{ site.baseurl }}/package.json">other dependencies</a> are bundled with the dist file.</p>
</div>
</div>
</div>
@ -219,11 +221,11 @@ prism: true
<div class="col-12@sm">
<h2>Be Social<a href="#be-social"></a></h2>
<div class="text-center">
<iframe src="http://ghbtns.com/github-btn.html?user=Vestride&repo=Shuffle&type=watch&count=true&size=large" height="30" width="170" frameborder="0" scrolling="0" style="width:170px; height: 30px;" allowTransparency="true" seamless></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=Vestride&repo=Shuffle&type=star&size=large" frameborder="0" scrolling="0" width="76px" height="30px"></iframe>
<a href="https://twitter.com/share" class="twitter-share-button" data-url="http://vestride.github.io/Shuffle/" data-via="Vestride" data-size="large">Tweet</a>
<a href="https://twitter.com/share" class="twitter-share-button" data-via="Vestride" data-size="large">Tweet</a>
<div class="g-plusone" data-href="http://vestride.github.io/Shuffle/" data-size="standard" data-annotation="bubble"></div>
<div class="g-plusone" data-annotation="none"></div>
</div>
</div>
</div>

@ -6,7 +6,7 @@ var Viewport = window.Viewport;
var Demo = function () {
this.element = document.getElementById('grid');
this.gridItems = this.element.querySelectorAll('.picture-item');
var sizer = this.element.querySelector('.shuffle__sizer');
var sizer = this.element.querySelector('.my-sizer-element');
this.shuffle = new Shuffle(this.element, {
itemSelector: '.picture-item',

@ -10,7 +10,7 @@ var Demo = function (element) {
this.shuffle = new Shuffle(element, {
itemSelector: '.picture-item',
sizer: element.querySelector('.shuffle__sizer'),
sizer: element.querySelector('.my-sizer-element'),
});
this._activeFilters = [];
@ -44,8 +44,6 @@ Demo.prototype.addShuffleEventListeners = function () {
console.log('type: %s', event.type, 'detail:', event.detail);
};
this.element.addEventListener(Shuffle.EventType.LOADING, handler, false);
this.element.addEventListener(Shuffle.EventType.DONE, handler, false);
this.element.addEventListener(Shuffle.EventType.LAYOUT, handler, false);
this.element.addEventListener(Shuffle.EventType.REMOVED, handler, false);
};

@ -2,5 +2,5 @@ var Shuffle = window.shuffle;
window.myShuffle = new Shuffle(document.querySelector('.my-grid-with-images'), {
itemSelector: '.js-item',
sizer: '.shuffle__sizer',
sizer: '.my-sizer-element',
});

@ -2,5 +2,5 @@
window.demo = new window.shuffle(document.getElementById('grid'), {
itemSelector: '.grid__brick',
sizer: document.querySelector('#grid .shuffle__sizer'),
sizer: document.querySelector('#grid .my-sizer-element'),
});

@ -27,7 +27,7 @@ export function onTransitionEnd(element, callback) {
export function cancelTransitionEnd(id) {
if (transitions[id]) {
transitions[id].element.removeEventListener(eventName, transitions[id].listener);
delete transitions[id];
transitions[id] = null;
return true;
}

@ -24,10 +24,6 @@ function each(obj, iterator, context) {
}
}
function defer(fn, context, wait) {
return setTimeout(fn.bind(context), wait);
}
function arrayMax(array) {
return Math.max.apply(Math, array);
}
@ -68,7 +64,7 @@ class Shuffle {
this.isDestroyed = false;
this.isInitialized = false;
this._transitions = [];
this._isMovementCanceled = false;
this.isTransitioning = false;
this._queue = [];
element = this._getElementOption(element);
@ -80,15 +76,8 @@ class Shuffle {
this.element = element;
this.id = 'shuffle_' + id++;
this._dispatch(Shuffle.EventType.LOADING);
this._init();
// Dispatch the done event asynchronously so that people can bind to it after
// Shuffle has been initialized.
defer(function () {
this.isInitialized = true;
this._dispatch(Shuffle.EventType.DONE);
}, this, 16);
this.isInitialized = true;
}
_init() {
@ -124,12 +113,13 @@ class Shuffle {
// Kick off!
this.filter(this.options.group, this.options.initialSort);
// The shuffle items haven't had transitions set on them yet
// so the user doesn't see the first layout. Set them now that the first layout is done.
defer(function () {
this._setTransitions();
this.element.style.transition = 'height ' + this.options.speed + 'ms ' + this.options.easing;
}, this);
// The shuffle items haven't had transitions set on them yet so the user
// doesn't see the first layout. Set them now that the first layout is done.
// First, however, a synchronous layout must be caused for the previous
// styles to be applied without transitions.
this.element.offsetWidth; // jshint ignore: line
this._setTransitions();
this.element.style.transition = 'height ' + this.options.speed + 'ms ' + this.options.easing;
}
/**
@ -381,16 +371,16 @@ class Shuffle {
var size;
// If the columnWidth property is a function, then the grid is fluid
if (typeof this.columnWidth === 'function') {
size = this.columnWidth(containerWidth);
if (typeof this.options.columnWidth === 'function') {
size = this.options.columnWidth(containerWidth);
// columnWidth option isn't a function, are they using a sizing element?
} else if (this.useSizer) {
size = Shuffle.getSize(this.options.sizer).width;
// if not, how about the explicitly set option?
} else if (this.columnWidth) {
size = this.columnWidth;
} else if (this.options.columnWidth) {
size = this.options.columnWidth;
// or use the size of the first item
} else if (this.items.length > 0) {
@ -479,6 +469,10 @@ class Shuffle {
* @return {boolean} Whether the event was prevented or not.
*/
_dispatch(name, details = {}) {
if (this.isDestroyed) {
return;
}
details.shuffle = this;
return !this.element.dispatchEvent(new CustomEvent(name, {
bubbles: true,
@ -747,16 +741,15 @@ class Shuffle {
_transition(opts) {
opts.item.applyCss(this._getStylesForTransition(opts));
this._whenTransitionDone(opts.item.element, opts.callback);
return this._whenTransitionDone(opts.item.element, opts.callback);
}
/**
* Execute the styles gathered in the style queue. This applies styles to elements,
* triggering transitions.
* @param {boolean} withLayout Whether to trigger a layout event.
* @private
*/
_processQueue(withLayout = true) {
_processQueue() {
if (this.isTransitioning) {
this._cancelMovement();
}
@ -779,8 +772,8 @@ class Shuffle {
// A call to layout happened, but none of the newly filtered items will
// change position. Asynchronously fire the callback here.
} else if (withLayout) {
defer(this._dispatchLayout, this);
} else {
setTimeout(this._dispatchLayout.bind(this), 0);
}
// Remove everything in the style queue
@ -1138,8 +1131,6 @@ Shuffle.FILTER_ATTRIBUTE_KEY = 'groups';
* @enum {string}
*/
Shuffle.EventType = {
LOADING: 'shuffle:loading',
DONE: 'shuffle:done',
LAYOUT: 'shuffle:layout',
REMOVED: 'shuffle:removed',
};
@ -1161,7 +1152,8 @@ Shuffle.options = {
// e.g. '.picture-item'.
itemSelector: '*',
// Sizer element. Use an element to determine the size of columns and gutters.
// Element or selector string. Use an element to determine the size of columns
// and gutters.
sizer: null,
// A static number or function that tells the plugin how wide the gutters
@ -1198,7 +1190,7 @@ Shuffle.options = {
// Transition delay offset for each item in milliseconds.
staggerAmount: 15,
// It can look a little weird when the last element is in the top row
// Maximum stagger delay in milliseconds.
staggerAmountMax: 250,
// Whether to use transforms or absolute positioning.

@ -49,7 +49,7 @@ describe('shuffle', function () {
}
}
function cleanup() {
function removeFixture() {
if (instance && instance.element) {
instance.destroy();
}
@ -62,19 +62,6 @@ describe('shuffle', function () {
fixture = null;
}
function removeFixture(done) {
if (!instance || instance.isInitialized) {
cleanup();
done();
} else {
instance.element.addEventListener(Shuffle.EventType.DONE, function onDone() {
instance.element.removeEventListener(Shuffle.EventType.DONE, onDone);
cleanup();
done();
});
}
}
function once(element, eventType, fn) {
var handler = function (e) {
element.removeEventListener(eventType, handler);
@ -103,12 +90,12 @@ describe('shuffle', function () {
appendFixture('regular').then(done);
});
afterEach(function (done) {
removeFixture(done);
afterEach(function () {
Shuffle.prototype._whenTransitionDone.restore();
removeFixture();
});
it('should have default options', function (done) {
it('should have default options', function () {
instance = new Shuffle(fixture);
expect(instance.items.length).to.equal(10);
expect(instance.visibleItems).to.equal(10);
@ -124,12 +111,7 @@ describe('shuffle', function () {
expect(instance.useSizer).to.equal(false);
expect(instance.id).to.equal('shuffle_0');
expect(instance.isInitialized).to.be.false;
instance.element.addEventListener(Shuffle.EventType.DONE, function onDone() {
instance.element.removeEventListener(Shuffle.EventType.DONE, onDone);
expect(instance.isInitialized).to.be.true;
done();
});
expect(instance.isInitialized).to.be.true;
});
it('should add classes and default styles', function () {
@ -233,11 +215,6 @@ describe('shuffle', function () {
speed: 0,
});
function first() {
once(fixture, Shuffle.EventType.LAYOUT, second);
instance.filter('design');
}
function second() {
expect(instance.visibleItems).to.equal(3);
var concealed = [3, 4, 5, 6, 7, 8, 10].map(function (num) {
@ -287,7 +264,8 @@ describe('shuffle', function () {
done();
}
once(fixture, Shuffle.EventType.DONE, first);
once(fixture, Shuffle.EventType.LAYOUT, second);
instance.filter('design');
});
it('can initialize filtered and the category parameter is optional', function () {
@ -314,159 +292,146 @@ describe('shuffle', function () {
expect(instance.lastSort).to.deep.equal(sortObj);
});
describe('after initialized', function () {
var clock;
beforeEach(function () {
clock = sinon.useFakeTimers();
instance = new Shuffle(fixture);
clock.tick(17);
expect(instance.isInitialized).to.be.true;
});
afterEach(function () {
clock.restore();
});
it('can calculate column spans', function () {
instance = new Shuffle(fixture);
expect(instance._getColumnSpan(50, 100, 3)).to.equal(1);
expect(instance._getColumnSpan(200, 100, 3)).to.equal(2);
expect(instance._getColumnSpan(200, 200, 3)).to.equal(1);
expect(instance._getColumnSpan(300, 100, 3)).to.equal(3);
it('can calculate column spans', function () {
expect(instance._getColumnSpan(50, 100, 3)).to.equal(1);
expect(instance._getColumnSpan(200, 100, 3)).to.equal(2);
expect(instance._getColumnSpan(200, 200, 3)).to.equal(1);
expect(instance._getColumnSpan(300, 100, 3)).to.equal(3);
// Column span should not be larger than the number of columns.
expect(instance._getColumnSpan(300, 50, 3)).to.equal(3);
// Column span should not be larger than the number of columns.
expect(instance._getColumnSpan(300, 50, 3)).to.equal(3);
// Fix for percentage values.
expect(instance._getColumnSpan(100.02, 100, 4)).to.equal(1);
expect(instance._getColumnSpan(99.98, 100, 4)).to.equal(1);
});
// Fix for percentage values.
expect(instance._getColumnSpan(100.02, 100, 4)).to.equal(1);
expect(instance._getColumnSpan(99.98, 100, 4)).to.equal(1);
});
it('can calculate column sets', function () {
instance = new Shuffle(fixture);
it('can calculate column sets', function () {
// _getColumnSet(columnSpan, columns)
instance.positions = [150, 0, 0, 0];
expect(instance._getColumnSet(1, 4)).to.deep.equal([150, 0, 0, 0]);
expect(instance._getColumnSet(2, 4)).to.deep.equal([150, 0, 0]);
});
// _getColumnSet(columnSpan, columns)
instance.positions = [150, 0, 0, 0];
expect(instance._getColumnSet(1, 4)).to.deep.equal([150, 0, 0, 0]);
expect(instance._getColumnSet(2, 4)).to.deep.equal([150, 0, 0]);
});
it('can get an element option', function () {
var first = fixture.firstElementChild;
expect(instance._getElementOption(first)).to.equal(first);
expect(instance._getElementOption('#item1')).to.equal(first);
expect(instance._getElementOption('#hello-world')).to.be.null;
expect(instance._getElementOption(null)).to.be.null;
expect(instance._getElementOption(undefined)).to.be.null;
expect(instance._getElementOption(function () {
return first;
})).to.be.null;
});
it('can get an element option', function () {
instance = new Shuffle(fixture);
var first = fixture.firstElementChild;
expect(instance._getElementOption(first)).to.equal(first);
expect(instance._getElementOption('#item1')).to.equal(first);
expect(instance._getElementOption('#hello-world')).to.be.null;
expect(instance._getElementOption(null)).to.be.null;
expect(instance._getElementOption(undefined)).to.be.null;
expect(instance._getElementOption(function () {
return first;
})).to.be.null;
});
it('can test elements against filters', function () {
it('can test elements against filters', function () {
instance = new Shuffle(fixture);
var first = fixture.firstElementChild;
expect(instance._doesPassFilter('design', first)).to.be.true;
expect(instance._doesPassFilter('black', first)).to.be.false;
var first = fixture.firstElementChild;
expect(instance._doesPassFilter('design', first)).to.be.true;
expect(instance._doesPassFilter('black', first)).to.be.false;
expect(instance._doesPassFilter(function (element) {
expect(element).to.exist;
return element.getAttribute('data-age') === '21';
}, first)).to.equal(true);
expect(instance._doesPassFilter(function (element) {
expect(element).to.exist;
return element.getAttribute('data-age') === '21';
}, first)).to.equal(true);
expect(instance._doesPassFilter(function (element) {
return element.getAttribute('data-age') === '22';
}, first)).to.equal(false);
});
expect(instance._doesPassFilter(function (element) {
return element.getAttribute('data-age') === '22';
}, first)).to.equal(false);
});
it('will maintain the last sort object', function () {
var initialSort = instance.lastSort;
it('will maintain the last sort object', function () {
instance = new Shuffle(fixture);
var initialSort = instance.lastSort;
instance.sort();
expect(instance.lastSort).to.deep.equal(initialSort);
instance.sort();
expect(instance.lastSort).to.deep.equal(initialSort);
instance.sort({ glen: true });
expect(instance.lastSort).to.deep.equal({ glen: true });
instance.sort({ glen: true });
expect(instance.lastSort).to.deep.equal({ glen: true });
instance.sort();
expect(instance.lastSort).to.deep.equal({ glen: true });
instance.sort();
expect(instance.lastSort).to.deep.equal({ glen: true });
});
});
it('should reset columns', function () {
it('should reset columns', function () {
instance = new Shuffle(fixture);
expect(instance.cols).to.be.above(0);
instance._resetCols();
expect(instance.cols).to.be.above(0);
instance._resetCols();
var positions = new Array(instance.cols);
for (var i = 0; i < instance.cols; i++) {
positions[i] = 0;
}
var positions = new Array(instance.cols);
for (var i = 0; i < instance.cols; i++) {
positions[i] = 0;
}
expect(instance.positions).to.deep.equal(positions);
});
expect(instance.positions).to.deep.equal(positions);
});
it('should destroy properly', function () {
instance.destroy();
it('should destroy properly', function () {
instance = new Shuffle(fixture);
instance.destroy();
expect(instance.element).to.be.null;
expect(instance.items).to.be.null;
expect(instance.options.sizer).to.be.null;
expect(instance.isDestroyed).to.be.true;
expect(instance.element).to.be.null;
expect(instance.items).to.be.null;
expect(instance.options.sizer).to.be.null;
expect(instance.isDestroyed).to.be.true;
expect(fixture).to.not.have.class('shuffle');
expect(fixture).to.not.have.class('shuffle');
toArray(fixture.children).forEach(function (child) {
expect(child).to.not.have.class('shuffle-item');
expect(child).to.not.have.class('filtered');
expect(child).to.not.have.class('concealed');
});
toArray(fixture.children).forEach(function (child) {
expect(child).to.not.have.class('shuffle-item');
expect(child).to.not.have.class('filtered');
expect(child).to.not.have.class('concealed');
});
});
it('should not update or shuffle when disabled or destroyed', function () {
var update = sinon.spy(instance, 'update');
var _filter = sinon.spy(instance, '_filter');
it('should not update or shuffle when disabled or destroyed', function () {
instance = new Shuffle(fixture);
var update = sinon.spy(instance, 'update');
var _filter = sinon.spy(instance, '_filter');
instance.disable();
instance.disable();
instance.filter('design');
instance.filter('design');
expect(_filter.called).to.be.false;
expect(update.called).to.be.false;
expect(_filter.called).to.be.false;
expect(update.called).to.be.false;
instance.enable(false);
instance.enable(false);
instance.destroy();
instance._onResize();
expect(update.called).to.be.false;
});
instance.destroy();
instance._onResize();
expect(update.called).to.be.false;
});
it('should not update when the container is the same size', function () {
var update = sinon.spy(instance, 'update');
it('should not update when the container is the same size', function () {
instance = new Shuffle(fixture);
var update = sinon.spy(instance, 'update');
instance._onResize();
instance._onResize();
expect(update.called).to.be.false;
});
expect(update.called).to.be.false;
});
describe('removing elements', function () {
var itemsToRemove;
var onDone;
var onRemoved;
beforeEach(function () {
var children = toArray(fixture.children);
itemsToRemove = children.slice(0, 2);
onDone = function () {
once(fixture, Shuffle.EventType.REMOVED, onRemoved);
instance.remove(itemsToRemove);
};
});
afterEach(function () {
itemsToRemove = null;
onDone = null;
onRemoved = null;
});
it('can remove items', function (done) {
@ -474,17 +439,20 @@ describe('shuffle', function () {
speed: 16,
});
onRemoved = function (evt) {
once(fixture, Shuffle.EventType.REMOVED, function (evt) {
var detail = evt.detail;
expect(detail.shuffle.visibleItems).to.equal(8);
expect(detail.collection[0].id).to.equal('item1');
expect(detail.collection[1].id).to.equal('item2');
expect(detail.shuffle.element.children).to.have.lengthOf(8);
expect(instance.isTransitioning).to.equal(false);
done();
};
once(fixture, Shuffle.EventType.DONE, onDone);
once(fixture, Shuffle.EventType.LAYOUT, function () {
done();
});
});
instance.remove(itemsToRemove);
});
it('can remove items without transforms', function (done) {
@ -493,17 +461,20 @@ describe('shuffle', function () {
useTransforms: false,
});
onRemoved = function (evt) {
once(fixture, Shuffle.EventType.REMOVED, function (evt) {
var detail = evt.detail;
expect(detail.shuffle.visibleItems).to.equal(8);
expect(detail.collection[0].id).to.equal('item1');
expect(detail.collection[1].id).to.equal('item2');
expect(detail.shuffle.element.children).to.have.lengthOf(8);
expect(detail.shuffle.isTransitioning).to.equal(false);
done();
};
once(fixture, Shuffle.EventType.DONE, onDone);
once(fixture, Shuffle.EventType.LAYOUT, function () {
done();
});
});
instance.remove(itemsToRemove);
});
});
@ -535,7 +506,6 @@ describe('shuffle', function () {
});
describe('inserting elements', function () {
var clock;
var items = [];
beforeEach(function () {
@ -555,18 +525,14 @@ describe('shuffle', function () {
items.push(eleven, twelve);
clock = sinon.useFakeTimers();
instance = new Shuffle(fixture, {
speed: 100,
group: 'black',
});
clock.tick(17);
expect(instance.isInitialized).to.be.true;
});
afterEach(function (done) {
once(fixture, Shuffle.EventType.LAYOUT, function () {
clock.restore();
items.length = 0;
done();
});
@ -611,10 +577,10 @@ describe('shuffle', function () {
});
});
afterEach(function (done) {
afterEach(function () {
items.length = 0;
clone.length = 0;
removeFixture(done);
removeFixture();
});
it('will catch empty objects', function () {

Loading…
Cancel
Save