Added stagger option. More refactoring. Working home page demo again.
parent
efc1f8087a
commit
37e6a86ff3
File diff suppressed because it is too large
Load Diff
@ -1,161 +1,201 @@
|
||||
'use strict';
|
||||
|
||||
var DEMO = (function ($) {
|
||||
'use strict';
|
||||
|
||||
var $grid = $('#grid'),
|
||||
$filterOptions = $('.filter-options'),
|
||||
$sizer = $grid.find('.shuffle__sizer'),
|
||||
|
||||
init = function () {
|
||||
|
||||
// None of these need to be executed synchronously
|
||||
setTimeout(function () {
|
||||
listen();
|
||||
setupFilters();
|
||||
setupSorting();
|
||||
setupSearching();
|
||||
}, 100);
|
||||
|
||||
// You can subscribe to custom events.
|
||||
// shrink, shrunk, filter, filtered, sorted, load, done
|
||||
$grid.on('loading.shuffle done.shuffle layout.shuffle', function (evt, shuffle) {
|
||||
// Make sure the browser has a console
|
||||
if (window.console && window.console.log && typeof window.console.log === 'function') {
|
||||
console.log('Shuffle:', evt.type);
|
||||
}
|
||||
});
|
||||
|
||||
// instantiate the plugin
|
||||
$grid.shuffle({
|
||||
itemSelector: '.picture-item',
|
||||
sizer: $sizer,
|
||||
});
|
||||
|
||||
// Destroy it! o_O
|
||||
// $grid.shuffle('destroy');
|
||||
},
|
||||
|
||||
// Set up button clicks
|
||||
setupFilters = function () {
|
||||
var $btns = $filterOptions.children();
|
||||
$btns.on('click', function () {
|
||||
var $this = $(this),
|
||||
isActive = $this.hasClass('active'),
|
||||
group = isActive ? 'all' : $this.data('group');
|
||||
|
||||
// Hide current label, show current label in title
|
||||
if (!isActive) {
|
||||
$('.filter-options .active').removeClass('active');
|
||||
}
|
||||
|
||||
$this.toggleClass('active');
|
||||
|
||||
// Filter elements
|
||||
$grid.shuffle('shuffle', group);
|
||||
});
|
||||
|
||||
$btns = null;
|
||||
},
|
||||
|
||||
setupSorting = function () {
|
||||
// Sorting options
|
||||
$('.sort-options').on('change', function () {
|
||||
var sort = this.value,
|
||||
opts = {};
|
||||
|
||||
// We're given the element wrapped in jQuery
|
||||
if (sort === 'date-created') {
|
||||
opts = {
|
||||
reverse: true,
|
||||
by: function ($el) {
|
||||
return $el.data('date-created');
|
||||
},
|
||||
};
|
||||
} else if (sort === 'title') {
|
||||
opts = {
|
||||
by: function ($el) {
|
||||
return $el.data('title').toLowerCase();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filter elements
|
||||
$grid.shuffle('sort', opts);
|
||||
});
|
||||
},
|
||||
|
||||
setupSearching = function () {
|
||||
// Advanced filtering
|
||||
$('.js-shuffle-search').on('keyup change', function () {
|
||||
var val = this.value.toLowerCase();
|
||||
$grid.shuffle('shuffle', function ($el, shuffle) {
|
||||
|
||||
// Only search elements in the current group
|
||||
if (shuffle.group !== 'all' && $.inArray(shuffle.group, $el.data('groups')) === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var text = $.trim($el.find('.picture-item__title').text()).toLowerCase();
|
||||
return text.indexOf(val) !== -1;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Re layout shuffle when images load. This is only needed
|
||||
// below 768 pixels because the .picture-item height is auto and therefore
|
||||
// the height of the picture-item is dependent on the image
|
||||
// I recommend using imagesloaded to determine when an image is loaded
|
||||
// but that doesn't support IE7
|
||||
listen = function () {
|
||||
var debouncedLayout = $.throttle(300, function () {
|
||||
$grid.shuffle('update');
|
||||
});
|
||||
|
||||
// Get all images inside shuffle
|
||||
$grid.find('img').each(function () {
|
||||
var proxyImage;
|
||||
|
||||
// Image already loaded
|
||||
if (this.complete && this.naturalWidth !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If none of the checks above matched, simulate loading on detached element.
|
||||
proxyImage = new Image();
|
||||
$(proxyImage).on('load', function () {
|
||||
$(this).off('load');
|
||||
debouncedLayout();
|
||||
});
|
||||
|
||||
proxyImage.src = this.src;
|
||||
});
|
||||
|
||||
// Because this method doesn't seem to be perfect.
|
||||
setTimeout(function () {
|
||||
debouncedLayout();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
return {
|
||||
init: init,
|
||||
};
|
||||
}(jQuery));
|
||||
var Shuffle = window.Shuffle;
|
||||
|
||||
// $(document).ready(function() {
|
||||
// DEMO.init();
|
||||
// });
|
||||
var Demo = function (element) {
|
||||
this.element = element;
|
||||
|
||||
var Shuffle = window.Shuffle;
|
||||
// Log out events.
|
||||
this.addShuffleEventListeners();
|
||||
|
||||
var Demo = function () {
|
||||
var element = document.getElementById('grid');
|
||||
this.shuffle = new Shuffle(element, {
|
||||
itemSelector: '.picture-item',
|
||||
sizer: element.querySelector('.shuffle__sizer'),
|
||||
});
|
||||
|
||||
this._activeFilters = [];
|
||||
|
||||
this.addFilterButtons();
|
||||
this.addSorting();
|
||||
this.addSearchFilter();
|
||||
this.listenForImageLoads();
|
||||
|
||||
this.mode = 'exclusive';
|
||||
};
|
||||
|
||||
Demo.prototype.toArray = function (arrayLike) {
|
||||
return Array.prototype.slice.call(arrayLike);
|
||||
};
|
||||
|
||||
Demo.prototype.toggleMode = function () {
|
||||
if (this.mode === 'additive') {
|
||||
this.mode = 'exclusive';
|
||||
} else {
|
||||
this.mode = 'additive';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Shuffle uses the CustomEvent constructor to dispatch events. You can listen
|
||||
* for them like you normally would (with jQuery for example). The extra event
|
||||
* data is in the `detail` property.
|
||||
*/
|
||||
Demo.prototype.addShuffleEventListeners = function () {
|
||||
var handler = function (event) {
|
||||
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);
|
||||
};
|
||||
|
||||
Demo.prototype.addFilterButtons = function () {
|
||||
var options = document.querySelector('.filter-options');
|
||||
|
||||
if (!options) {
|
||||
return;
|
||||
}
|
||||
|
||||
var filterButtons = this.toArray(
|
||||
options.children
|
||||
);
|
||||
|
||||
filterButtons.forEach(function (button) {
|
||||
button.addEventListener('click', this._handleFilterClick.bind(this), false);
|
||||
}, this);
|
||||
};
|
||||
|
||||
Demo.prototype._handleFilterClick = function (evt) {
|
||||
var btn = evt.currentTarget;
|
||||
var isActive = btn.classList.contains('active');
|
||||
var btnGroup = btn.getAttribute('data-group');
|
||||
|
||||
// You don't need _both_ of these modes. This is only for the demo.
|
||||
|
||||
// For this custom 'additive' mode in the demo, clicking on filter buttons
|
||||
// doesn't remove any other filters.
|
||||
if (this.mode === 'additive') {
|
||||
// If this button is already active, remove it from the list of filters.
|
||||
if (isActive) {
|
||||
this._activeFilters.splice(this._activeFilters.indexOf(btnGroup));
|
||||
} else {
|
||||
this._activeFilters.push(btnGroup);
|
||||
}
|
||||
|
||||
btn.classList.toggle('active');
|
||||
|
||||
// Filter elements
|
||||
this.shuffle.filter(this._activeFilters);
|
||||
|
||||
// 'exclusive' mode lets only one filter button be active at a time.
|
||||
} else {
|
||||
this._removeActiveClassFromChildren(btn.parentNode);
|
||||
|
||||
var filterGroup;
|
||||
if (isActive) {
|
||||
btn.classList.remove('active');
|
||||
filterGroup = Shuffle.ALL_ITEMS;
|
||||
} else {
|
||||
btn.classList.add('active');
|
||||
filterGroup = btnGroup;
|
||||
}
|
||||
|
||||
this.shuffle.filter(filterGroup);
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype._removeActiveClassFromChildren = function (parent) {
|
||||
var children = parent.children;
|
||||
for (var i = children.length - 1; i >= 0; i--) {
|
||||
children[i].classList.remove('active');
|
||||
}
|
||||
};
|
||||
|
||||
Demo.prototype.addSorting = function () {
|
||||
var menu = document.querySelector('.sort-options');
|
||||
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
|
||||
menu.addEventListener('change', this._handleSortChange.bind(this));
|
||||
};
|
||||
|
||||
Demo.prototype._handleSortChange = function (evt) {
|
||||
var value = evt.target.value;
|
||||
var options = {};
|
||||
|
||||
function sortByDate(element) {
|
||||
return element.getAttribute('data-created');
|
||||
}
|
||||
|
||||
function sortByTitle(element) {
|
||||
return element.getAttribute('data-title').toLowerCase();
|
||||
}
|
||||
|
||||
if (value === 'date-created') {
|
||||
options = {
|
||||
reverse: true,
|
||||
by: sortByDate,
|
||||
};
|
||||
} else if (value === 'title') {
|
||||
options = {
|
||||
by: sortByTitle,
|
||||
};
|
||||
}
|
||||
|
||||
this.shuffle.sort(options);
|
||||
};
|
||||
|
||||
// Advanced filtering
|
||||
Demo.prototype.addSearchFilter = function () {
|
||||
var searchInput = document.querySelector('.js-shuffle-search');
|
||||
|
||||
if (!searchInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
searchInput.addEventListener('keyup', this._handleSearchKeyup.bind(this));
|
||||
};
|
||||
|
||||
Demo.prototype._handleSearchKeyup = function (evt) {
|
||||
var searchText = evt.target.value.toLowerCase();
|
||||
|
||||
this.shuffle.filter(function (element, shuffle) {
|
||||
// Get the item's groups.
|
||||
var groups = JSON.parse(element.getAttribute('data-groups'));
|
||||
|
||||
// Only search elements in the current group
|
||||
if (shuffle.group !== 'all' && groups.indexOf(shuffle.group) === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var title = element.querySelector('.picture-item__title');
|
||||
var titleText = title.textContent.toLowerCase().trim();
|
||||
|
||||
return titleText.indexOf(searchText) !== -1;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Re-layout shuffle when images load. This is only needed below 768 pixels
|
||||
* because the .picture-item height is auto and therefore the height of the
|
||||
* picture-item is dependent on the image. I recommend using imagesloaded by
|
||||
* desandro to determine when all your images have loaded.
|
||||
*/
|
||||
Demo.prototype.listenForImageLoads = function () {
|
||||
var imgs = this.element.querySelectorAll('img');
|
||||
var handler = function () {
|
||||
this.shuffle.update();
|
||||
}.bind(this);
|
||||
|
||||
for (var i = imgs.length - 1; i >= 0; i--) {
|
||||
imgs[i].addEventListener('load', handler, false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
new Demo();
|
||||
window.demo = new Demo(document.getElementById('grid'));
|
||||
});
|
||||
|
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
export default function assign(target) {
|
||||
var output = Object(target);
|
||||
for (var i = 1, length = arguments.length; i < length; i++) {
|
||||
var source = arguments[i];
|
||||
if (source !== undefined && source !== null) {
|
||||
for (var key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
output[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
|
||||
let element = document.body || document.documentElement;
|
||||
let e = document.createElement('div');
|
||||
e.style.cssText = 'width:10px;padding:2px;box-sizing:border-box;';
|
||||
element.appendChild(e);
|
||||
|
||||
let width = window.getComputedStyle(e, null).width;
|
||||
let ret = width === '10px';
|
||||
|
||||
element.removeChild(e);
|
||||
|
||||
export default ret;
|
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
import getNumber from './get-number';
|
||||
import COMPUTED_SIZE_INCLUDES_PADDING from './computed-size';
|
||||
|
||||
/**
|
||||
* Retrieve the computed style for an element, parsed as a float.
|
||||
* @param {Element} element Element to get style for.
|
||||
* @param {string} style Style property.
|
||||
* @param {CSSStyleDeclaration} [styles] Optionally include clean styles to
|
||||
* use instead of asking for them again.
|
||||
* @return {number} The parsed computed value or zero if that fails because IE
|
||||
* will return 'auto' when the element doesn't have margins instead of
|
||||
* the computed style.
|
||||
* @private
|
||||
*/
|
||||
export default function getNumberStyle(element, style,
|
||||
styles = window.getComputedStyle(element, null)) {
|
||||
var value = getNumber(styles[style]);
|
||||
|
||||
// Support IE<=11 and W3C spec.
|
||||
if (!COMPUTED_SIZE_INCLUDES_PADDING && style === 'width') {
|
||||
value += getNumber(styles.paddingLeft) +
|
||||
getNumber(styles.paddingRight) +
|
||||
getNumber(styles.borderLeftWidth) +
|
||||
getNumber(styles.borderRightWidth);
|
||||
} else if (!COMPUTED_SIZE_INCLUDES_PADDING && style === 'height') {
|
||||
value += getNumber(styles.paddingTop) +
|
||||
getNumber(styles.paddingBottom) +
|
||||
getNumber(styles.borderTopWidth) +
|
||||
getNumber(styles.borderBottomWidth);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
// Underscore's throttle method.
|
||||
export default function(func, wait, options) {
|
||||
var _this;
|
||||
var args;
|
||||
var result;
|
||||
var timeout = null;
|
||||
var previous = 0;
|
||||
if (!options) options = {};
|
||||
var later = function () {
|
||||
previous = options.leading === false ? 0 : Date.now();
|
||||
timeout = null;
|
||||
result = func.apply(_this, args);
|
||||
if (!timeout) _this = args = null;
|
||||
};
|
||||
|
||||
return function () {
|
||||
var now = Date.now();
|
||||
if (!previous && options.leading === false) previous = now;
|
||||
var remaining = wait - (now - previous);
|
||||
_this = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
previous = now;
|
||||
result = func.apply(_this, args);
|
||||
if (!timeout) _this = args = null;
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue