blob: 855b7b2fa17017aa72640da40ccd66d0fcbdd5f6 [file] [log] [blame]
/**
* @file 最简单的tab。支持双级tab。支持tab动态增删。
* @author sushuang(sushuang@baidu.com)
* @date 2014-04
*/
define(function (require) {
var $ = require('jquery');
var lib = require('../lib');
var Component = require('./Component');
var libPeek = lib.peek;
var MAIN_CSS = 'cpt-tab';
/**
* 最简单的tab
* 模板中的声明方式:
* TODO
*
* 不负责tab内容的enter、leave,外层自行负责。只负责改变其css。
*/
var Tab = Component.extend({
_define: {
viewModel: function () {
return {
currentTab: lib.ob(), // 可初始时为空
currentTab1: lib.ob(), // 启用二级tab时指定
baseCss: MAIN_CSS,
tabAttr: null, // {string} 必须指定
tabAttr1: null, // {string} 必须指定
conAttr: null, // {string} 指定则启用二级tab
conAttr1: null // {string} 指定则启用二级tab
};
},
viewModelPublic: ['currentTab', 'currentTab1'],
suppressConstructSub: true, // tab 本身不拥有、不初始化子component。
css: MAIN_CSS
},
/**
* @override
*/
_prepare: function () {
var viewModel = this._viewModel();
var baseCss = viewModel.baseCss;
viewModel.headItemCss = baseCss + '-hi';
viewModel.currentHeadItemCss = baseCss + '-hi-curr';
viewModel.headItemCss1 = baseCss + '-hi1';
viewModel.currentHeadItemCss1 = baseCss + '-hi1-curr';
viewModel.conItemCss = baseCss + '-coni';
viewModel.headItemDisabledCss = viewModel.headItemCss + '-disabled';
viewModel.conItemDisabledCss = viewModel.conItemCss + '-disabled';
},
/**
* @override
*/
_init: function () {
var viewModel = this._viewModel();
var currentTab = viewModel.currentTab;
var currentTab1 = viewModel.currentTab1;
// 理论上,需要保证,对currentTab、currentTab1的依赖是排在其所有依赖的最前面的
// 否则,currentTab改变时,面板没有show出现,就执行了别的逻辑,可能引发错误。
// FIXME
// 但是,现在并没有提供这种设定 响应函数 执行顺序的 接口。仅是靠实现时的注意:先初始化tab。
this._disposable(currentTab.subscribe(
function (nextTabKey) {
this._changeTabByModel(nextTabKey, libPeek(currentTab1));
},
this
));
this._useTab1() && this._disposable(currentTab1.subscribe(
function (nextTabKey1) {
this._changeTabByModel(libPeek(currentTab), nextTabKey1);
},
this
));
this.bind();
},
/**
* 如果执行了unbind,可重新执行bind,进行控件与dom的连接
* 但是viewModel中定义的东西不可变。
* 初始化时自动会bind。
*
* @public
* @throws {Error} 如果已经被bind,重复执行抛出错误。
*/
bind: function () {
if (this._prop('bound')) {
throw new Error('It has been bound.');
}
var viewModel = this._viewModel();
this._bindDom();
// 根据当前model状态,设置tab状态
this._changeTabByModel(
libPeek(viewModel.currentTab),
libPeek(viewModel.currentTab1)
);
this._bindModelUpdater();
this._prop('bound', true);
},
/**
* 此方法解除控件和dom的连接,执行此方法后,主元素内部的dom可由外部进行修改。
*
* @pubilc
*/
unbind: function () {
this._removeCurrSelect();
this._unbindModelUpdater();
this._unbindDom();
this._prop('bound', false);
},
/**
* 是否控件已和dom连接
*
* @public
* @return {boolean}
*/
isBound: function () {
return this._prop('bound');
},
/**
* 此返回值和viewModel.currentTab()的区别是,
* 假如currentTab设为超出范围的值,那么此方法返回空。
*
* @public
* @return {string} currentTabKey
*/
getCurrentTabKey: function () {
return this._prop('currentTabKey');
},
/**
* 类同getCurrentTabKey方法。
*
* @public
* @return {string} currentTabKey1
*/
getCurrentTabKey1: function () {
return this._prop('currentTabKey1');
},
/**
* 是否有此tab
*
* @public
* @param {string} tabKey
* @param {string=} tabKey1 当useTab1时须使用
* @return {boolean}
*/
hasTab: function (tabKey, tabKey1) {
return !!this._getTabWrap(tabKey, tabKey1);
},
/**
* 设置某个tab项是否可用,如果不可用,tab项和内容全部隐藏,就像不存在一样。
* 暂不支持tabKey1级别的disabled。
*
* @public
* @param {boolean} disabled 是否可用
* @param {string} tabKey 某tabKey
* @param {string=} tabKey1 某tabKey1
*/
setTabDisabled: function (disabled, tabKey, tabKey1) {
var viewModel = this._viewModel();
var currWrap = this._getTabWrap(tabKey, tabKey1);
currWrap.$tabEl[
disabled ? 'addClass' : 'removeClass'
](viewModel.headItemDisabledCss);
currWrap.$conEl[
disabled ? 'addClass' : 'removeClass'
](viewModel.conItemDisabledCss);
disable(currWrap.$conEl, disabled);
},
/**
* 判断是否可用
*
* @public
* @param {string} tabKey 某tabKey
* @param {string=} tabKey1 某tabKey1
* @return {boolean} 是否可用
*/
isTabDisabled: function (tabKey, tabKey1) {
var currWrap = this._getTabWrap(tabKey, tabKey1);
return currWrap
? currWrap.$tabEl.hasClass(this._viewModel().headItemDisabledCss)
: null;
},
/**
* 设置 tab 标签文字
*
* @public
* @param {string} key
* @param {string} html
*/
setTabLabel: function (key, html) {
var tabWraps = this._prop('tabWraps');
tabWraps[key].$tabEl[0].innerHTML = html;
},
/**
* @private
*/
_bindDom: function () {
var tabWraps = this._prop('tabWraps', {});
var $el = this.$el();
var viewModel = this._viewModel();
var tabAttr = viewModel.tabAttr;
var tabAttr1 = viewModel.tabAttr1;
var conAttr = viewModel.conAttr;
var conAttr1 = viewModel.conAttr1;
var useTab1 = this._useTab1();
var $tabEls = $el.find('*[data-' + tabAttr + ']');
var $tabEls1 = useTab1 && $el.find('*[data-' + tabAttr1 + ']');
var $conEls = $el.find('*[data-' + viewModel.conAttr + ']');
// 初始化tab节点
$tabEls.each(function (index, el) {
var $el = $(el);
$el.addClass(viewModel.headItemCss);
// 仅有一级的情况
if (!useTab1) {
tabWraps[$el.data(tabAttr)] = {$tabEl: $el};
}
// 有二级的情况
else {
$tabEls1.each(function (index, el1) {
var $el1 = $(el1);
$el1.addClass(viewModel.headItemCss1);
var tl = tabWraps[$el.data(tabAttr)]
|| (tabWraps[$el.data(tabAttr)] = {});
tl[$el1.data(tabAttr1)] = {$tabEl: $el, $tabEl1: $el1};
});
}
});
// 初始化con节点
$conEls.each(function (index, el) {
var $el = $(el);
$el.addClass(viewModel.conItemCss);
// 先所有con都隐藏
showOrHide($el, false);
// 仅有一级的情况
if (!useTab1) {
tabWraps[$el.data(conAttr)].$conEl = $el;
}
// 有二级的情况
else {
tabWraps[$el.data(conAttr)][$el.data(conAttr1)].$conEl = $el;
}
});
},
/**
* @private
*/
_unbindDom: function () {
this._prop('tabWraps', {});
// TODO
// 没有unbind css。
},
/**
* @private
*/
_getTabWrap: function (tabKey, tabKey1) {
var tabWrap = this._prop('tabWraps')[tabKey];
if (this._useTab1() && tabWrap) {// nextWrap可能为空,因为con允许暂时不全。
tabWrap = tabWrap[tabKey1];
}
return tabWrap;
},
/**
* 去除当前选中
*
* @private
*/
_removeCurrSelect: function () {
var viewModel = this._viewModel();
var useTab1 = this._useTab1();
var currWrap = this._getTabWrap(
this._prop('currentTabKey'),
this._prop('currentTabKey1')
);
if (currWrap) {
currWrap.$tabEl.removeClass(viewModel.currentHeadItemCss);
useTab1 && currWrap.$tabEl1.removeClass(viewModel.currentHeadItemCss1);
showOrHide(currWrap.$conEl, false);
this._prop('currentTabKey', null);
useTab1 && this._prop('currentTabKey1', null);
}
},
/**
* @private
*/
_addSelect: function (nextTabKey, nextTabKey1) {
var viewModel = this._viewModel();
var useTab1 = this._useTab1();
var nextWrap = this._getTabWrap(nextTabKey, nextTabKey1);
// 添加当前选中
if (nextWrap) {
nextWrap.$tabEl.addClass(viewModel.currentHeadItemCss);
useTab1 && nextWrap.$tabEl1.addClass(viewModel.currentHeadItemCss1);
showOrHide(nextWrap.$conEl, true);
this._prop('currentTabKey', nextTabKey);
useTab1 && this._prop('currentTabKey1', nextTabKey1);
}
},
/**
* @private
*/
_changeTabByModel: function (nextTabKey, nextTabKey1) {
// 因为只做css加减操作,所以在这个change监听函数中,
// 就不判断disable了。这易于避免不一致发生。
// 先去除当前选中
this._removeCurrSelect();
// 添加选中
this._addSelect(nextTabKey, nextTabKey1);
},
/**
* @private
*/
_bindModelUpdater: function () {
var viewModel = this._viewModel();
var tabAttr = viewModel.tabAttr;
var tabAttr1 = viewModel.tabAttr1;
var that = this;
var $main = this.$el();
// 一级tab
$main.on(
this._event('click'),
'*[data-' + tabAttr + ']',
function () {
var nextTabKey = $(this).data(tabAttr);
if (nextTabKey !== that._prop('currentTabKey')) {
viewModel.currentTab(nextTabKey);
}
}
);
// 二级tab
$main.on(
this._event('click'),
'*[data-' + tabAttr1 + ']',
function () {
var nextTabKey1 = $(this).data(tabAttr1);
if (nextTabKey1 !== that._prop('currentTabKey1')) {
viewModel.currentTab1(nextTabKey1);
}
}
);
},
/**
* @private
*/
_unbindModelUpdater: function () {
this.$el().off(this._event());
},
/**
* 是否启用二级tab
*
* @private
*/
_useTab1: function () {
return this._viewModel().tabAttr1 != null;
},
/**
* @override
*/
_dispose: function () {
this._prop('tabWraps', null);
}
});
/**
* @inner
*/
function showOrHide($el, showOrHide) {
var cpt = lib.getComponent($el);
cpt
? cpt.viewModel('visible')(showOrHide)
: $el[showOrHide ? 'show' : 'hide']();
}
/**
* @inner
*/
function disable($el, dis) {
var cpt = lib.getComponent($el);
cpt && cpt.viewModel('disabled')(dis);
}
return Tab;
});