Skip to content

Instantly share code, notes, and snippets.

@josephj
Created July 30, 2015 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josephj/f3f5a361f84cf56dca33 to your computer and use it in GitHub Desktop.
Save josephj/f3f5a361f84cf56dca33 to your computer and use it in GitHub Desktop.
/*global $ */
'use strict';
var Stackla = require('./stackla.js'),
Base = require('./base.js'),
Sortable = null,
Mustache = require('mustache'),
// Constant
NAME = 'sortable',
ORDER_BY_ATTR = 'data-sortable-orderby',
SELECTOR = '[data-uikit=sortable]',
SORT_BY_ATTR = 'data-sortable-sortby',
ACTIVE_CLASS = 'is-active',
LOADING_CLASS = 'is-loading',
WRAPPER_CLASS = 'st-sortable-wrapper',
FIELD_CLASS = 'st-sortable',
INDICATOR_CLASS = 'st-sortable-indicator',
UP_ICON_CLASS = 'fs fs-arrow-up5',
SORT_ICON_CLASS = 'fs fs-sort',
LOAD_ICON_CLASS = 'fs fs-spin fs-spinner',
DOWN_ICON_CLASS = 'fs fs-arrow-down5';
Sortable = Stackla.createClass(Base, {
initializer: function ($el, options) {
var that = this;
options = options || {};
// Validation
if (!options.url || !options.template || !options.target) {
throw new Error('The url, template, and target config options are required!');
}
// Public attributes
that.$el = $($el);
that.url = options.url;
that.template = $(options.template).html();
that.$target = $(options.target);
that.sortBy = null;
that.orderBy = null;
// Private attributes, just for internal use
that._isLoading = false;
that.$_active = null;
that.bind();
},
toString: function () {
return 'Sortable';
},
//=================
// Event Handlers
//=================
_handleClick: function (e) {
var that = this,
$current = $(e.currentTarget),
$prev = that.$_active;
// Stop if the previous request hasn't finished.
if (that._isLoading) {
return;
}
that.log('_handleClick() is executed');
// Reset previous active field
if (!$current.is($prev)) {
$prev.removeAttr(ORDER_BY_ATTR);
$prev.removeClass(ACTIVE_CLASS);
}
// Update direction
if ($current.attr(ORDER_BY_ATTR) === 'asc') {
$current.attr(ORDER_BY_ATTR, 'desc');
} else {
$current.attr(ORDER_BY_ATTR, 'asc');
}
that.$_active = $current;
that.sortBy = $current.attr(SORT_BY_ATTR);
that.orderBy = $current.attr(ORDER_BY_ATTR);
that._makeRequest();
},
//=================
// Private Methods
//=================
_makeRequest: function () {
var that = this ,
url = that.url + '?sort_by=' + encodeURIComponent(that.sortBy) +
'&order_by=' + encodeURIComponent(that.orderBy);
that.log('_makeRequest() is executed');
that.$_active.addClass(LOADING_CLASS + ' ' + ACTIVE_CLASS);
that._isLoading = true;
that._uiSetIndicator(that.$_active, 'loading');
$.ajax({
url: url,
dataType: 'json',
success: function (data) {
var html;
that.emit('load', [data]);
html = Mustache.render(that.template, data);
that.$target.html(html);
that._uiSetIndicator(that.$_active, that.orderBy);
},
error: function (xhr, status, err) {
that.emit('error', [err]);
},
complete: function () {
that.$_active.removeClass(LOADING_CLASS);
that._isLoading = false;
}
});
},
_uiSetIndicator: function ($sort, orderBy) {
var that = this,
$indicator;
that.log('_uiSetIndicator() is executed');
// Remove exisiting indicator
that.$el.find('.' + INDICATOR_CLASS).removeClass([
UP_ICON_CLASS,
DOWN_ICON_CLASS,
LOAD_ICON_CLASS
].join(' '));
that.$el.find('.' + INDICATOR_CLASS).addClass(SORT_ICON_CLASS);
$indicator = $sort.find('.' + INDICATOR_CLASS);
$indicator.removeClass(SORT_ICON_CLASS);
if (orderBy === 'desc') {
$indicator.addClass(DOWN_ICON_CLASS);
} else if (orderBy === 'asc') {
$indicator.addClass(UP_ICON_CLASS);
} else if (orderBy === 'loading') {
$indicator.addClass(LOAD_ICON_CLASS);
}
},
//===================
// Lifecycle Methods
//===================
bind: function () {
var that = this,
$el = that.$el;
that.log('bind() is executed');
$el.on('click', '.' + FIELD_CLASS, $.proxy(that._handleClick, that));
},
render: function () {
var that = this,
$el = that.$el,
$active,
$fields,
$field,
$indicator;
that.log('render() is executed');
$fields = that.$el.find('[' + SORT_BY_ATTR + ']');
$fields.addClass(FIELD_CLASS);
// Append indicator with up icon to all fields by default
$fields.each(function (i, el) {
$field = $(el);
$indicator = $field.find('.' + INDICATOR_CLASS);
if (!$indicator.length) {
$indicator = $('<i class="' + INDICATOR_CLASS + ' ' + SORT_ICON_CLASS + '"></i>');
$field.append($indicator);
}
});
// Apply correct class to active field
$active = $el.find('[' + ORDER_BY_ATTR + ']');
if ($active.length) {
that.sortBy = $active.attr(SORT_BY_ATTR);
that.orderBy = $active.attr(ORDER_BY_ATTR);
if (that.orderBy === 'desc') {
$active.find('.' + INDICATOR_CLASS).removeClass([SORT_ICON_CLASS, UP_ICON_CLASS].join(' ')).addClass(DOWN_ICON_CLASS);
}
$active.addClass(ACTIVE_CLASS);
}
that.$_active = $active;
// Add class to wrapper
that.$el.addClass(WRAPPER_CLASS);
}
}, 'Sortable');
Stackla.smartBind({
pluginName: NAME,
selector: SELECTOR,
classFn: Stackla.Sortable,
autoBind: true
});
module.exports = Stackla.Sortable;
'use strict';
var React = require('react'),
LogMixin = require('js/common/log-mixin');
module.exports = React.createClass({
mixins: [LogMixin],
toString: function () {
return 'Sortable';
},
propTypes: {
active: React.PropTypes.bool,
disabled: React.PropTypes.bool,
orderBy: React.PropTypes.string,
orderBydirection: React.PropTypes.string,
onClick: React.PropTypes.func
},
getDefaultProps: function () {
return {
active: false,
disabled: false,
orderBy: 'id',
orderByDirection: 'desc'
}
},
handleClick: function (orderBy, orderByDirection) {
var that = this;
that.log('handleClick() is executed');
that.props.onClick(orderBy, orderByDirection);
},
render: function () {
var that = this,
props = that.props,
linkProps = (props.disabled) ? {disabled: 'disabled'} : {},
direction = (props.orderByDirection === 'asc') ? 'up' : 'down',
className = 'Sortable',
iconClassName = ['Sortable-indicator'],
nextDirection = props.orderByDirection;
that.log('render() is executed');
if (props.active) {
nextDirection = (props.orderByDirection === 'asc') ? 'desc' : 'asc';
className += ' is-active';
iconClassName.push('fs', 'fs-arrow-' + direction + '5');
} else {
iconClassName.push('fs', 'fs-sort');
}
return (
<a {...linkProps} href="javascript:void(0)" className={className}
onClick={that.handleClick.bind(that, props.orderBy, nextDirection)}>
{props.children}{' '}
<i className={iconClassName.join(' ')}></i>
</a>
);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment