amazeui
Version:
Sleek, intuitive, and powerful front-end framework for faster and easier web development.
247 lines (196 loc) • 6.15 kB
JavaScript
'use strict';
var $ = require('jquery');
var UI = require('./core');
var Hammer = require('./util.hammer');
var supportTransition = UI.support.transition;
var animation = UI.support.animation;
/**
* @via https://github.com/twbs/bootstrap/blob/master/js/tab.js
* @copyright 2011-2014 Twitter, Inc.
* @license MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/**
* Tabs
* @param {HTMLElement} element
* @param {Object} options
* @constructor
*/
var Tabs = function(element, options) {
this.$element = $(element);
this.options = $.extend({}, Tabs.DEFAULTS, options || {});
this.transitioning = this.activeIndex = null;
this.refresh();
this.init();
};
Tabs.DEFAULTS = {
selector: {
nav: '> .am-tabs-nav',
content: '> .am-tabs-bd',
panel: '> .am-tab-panel'
},
activeClass: 'am-active'
};
Tabs.prototype.refresh = function() {
var selector = this.options.selector;
this.$tabNav = this.$element.find(selector.nav);
this.$navs = this.$tabNav.find('a');
this.$content = this.$element.find(selector.content);
this.$tabPanels = this.$content.find(selector.panel);
var $active = this.$tabNav.find('> .' + this.options.activeClass);
// Activate the first Tab when no active Tab or multiple active Tabs
if ($active.length !== 1) {
this.open(0);
} else {
this.activeIndex = this.$navs.index($active.children('a'));
}
};
Tabs.prototype.init = function() {
var _this = this;
var options = this.options;
this.$element.on('click.tabs.amui', options.selector.nav + ' a', function(e) {
e.preventDefault();
_this.open($(this));
});
// TODO: nested Tabs touch events
if (!options.noSwipe) {
if (!this.$content.length) {
return this;
}
var hammer = new Hammer.Manager(this.$content[0]);
var swipe = new Hammer.Swipe({
direction: Hammer.DIRECTION_HORIZONTAL
// threshold: 40
});
hammer.add(swipe);
hammer.on('swipeleft', UI.utils.debounce(function(e) {
e.preventDefault();
_this.goTo('next');
}, 100));
hammer.on('swiperight', UI.utils.debounce(function(e) {
e.preventDefault();
_this.goTo('prev');
}, 100));
this._hammer = hammer;
}
};
/**
* Open $nav tab
* @param {jQuery|HTMLElement|Number} $nav
* @returns {Tabs}
*/
Tabs.prototype.open = function($nav) {
var activeClass = this.options.activeClass;
var activeIndex = typeof $nav === 'number' ? $nav : this.$navs.index($($nav));
$nav = typeof $nav === 'number' ? this.$navs.eq(activeIndex) : $($nav);
if (!$nav ||
!$nav.length ||
this.transitioning ||
$nav.parent('li').hasClass(activeClass)) {
return;
}
var $tabNav = this.$tabNav;
var href = $nav.attr('href');
var regexHash = /^#.+$/;
var $target = regexHash.test(href) && this.$content.find(href) ||
this.$tabPanels.eq(activeIndex);
var previous = $tabNav.find('.' + activeClass + ' a')[0];
var e = $.Event('open.tabs.amui', {
relatedTarget: previous
});
$nav.trigger(e);
if (e.isDefaultPrevented()) {
return;
}
// activate Tab nav
this.activate($nav.closest('li'), $tabNav);
// activate Tab content
this.activate($target, this.$content, function() {
$nav.trigger({
type: 'opened.tabs.amui',
relatedTarget: previous
});
});
this.activeIndex = activeIndex;
};
Tabs.prototype.activate = function($element, $container, callback) {
this.transitioning = true;
var activeClass = this.options.activeClass;
var $active = $container.find('> .' + activeClass);
var transition = callback && supportTransition && !!$active.length;
$active.removeClass(activeClass + ' am-in');
$element.addClass(activeClass);
if (transition) {
$element.redraw(); // reflow for transition
$element.addClass('am-in');
} else {
$element.removeClass('am-fade');
}
var complete = $.proxy(function complete() {
callback && callback();
this.transitioning = false;
}, this);
transition && !this.$content.is('.am-tabs-bd-ofv') ?
$active.one(supportTransition.end, complete) : complete();
};
/**
* Go to `next` or `prev` tab
* @param {String} direction - `next` or `prev`
*/
Tabs.prototype.goTo = function(direction) {
var navIndex = this.activeIndex;
var isNext = direction === 'next';
var spring = isNext ? 'am-animation-right-spring' :
'am-animation-left-spring';
if ((isNext && navIndex + 1 >= this.$navs.length) || // last one
(!isNext && navIndex === 0)) { // first one
var $panel = this.$tabPanels.eq(navIndex);
animation && $panel.addClass(spring).on(animation.end, function() {
$panel.removeClass(spring);
});
} else {
this.open(isNext ? navIndex + 1 : navIndex - 1);
}
};
Tabs.prototype.destroy = function() {
this.$element.off('.tabs.amui');
Hammer.off(this.$content[0], 'swipeleft swiperight');
this._hammer && this._hammer.destroy();
$.removeData(this.$element, 'amui.tabs');
};
// Plugin
function Plugin(option) {
var args = Array.prototype.slice.call(arguments, 1);
var methodReturn;
this.each(function() {
var $this = $(this);
var $tabs = $this.is('.am-tabs') && $this || $this.closest('.am-tabs');
var data = $tabs.data('amui.tabs');
var options = $.extend({}, UI.utils.parseOptions($this.data('amTabs')),
$.isPlainObject(option) && option);
if (!data) {
$tabs.data('amui.tabs', (data = new Tabs($tabs[0], options)));
}
if (typeof option === 'string') {
if (option === 'open' && $this.is('.am-tabs-nav a')) {
data.open($this);
} else {
methodReturn = typeof data[option] === 'function' ?
data[option].apply(data, args) : data[option];
}
}
});
return methodReturn === undefined ? this : methodReturn;
}
$.fn.tabs = Plugin;
// Init code
UI.ready(function(context) {
$('[data-am-tabs]', context).tabs();
});
$(document).on('click.tabs.amui.data-api', '[data-am-tabs] .am-tabs-nav a',
function(e) {
e.preventDefault();
Plugin.call($(this), 'open');
});
module.exports = UI.tabs = Tabs;
// TODO: 1. Ajax 支持
// 2. touch 事件处理逻辑优化