You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
443 lines
12 KiB
443 lines
12 KiB
/*
|
|
YUI 3.17.2 (build 9c3c78e)
|
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
|
Licensed under the BSD License.
|
|
http://yuilibrary.com/license/
|
|
*/
|
|
|
|
YUI.add('tabview', function (Y, NAME) {
|
|
|
|
/**
|
|
* The TabView module
|
|
*
|
|
* @module tabview
|
|
*/
|
|
|
|
var DOT = '.',
|
|
|
|
/**
|
|
* Provides a tabbed widget interface
|
|
* @param config {Object} Object literal specifying tabview configuration properties.
|
|
*
|
|
* @class TabView
|
|
* @constructor
|
|
* @extends Widget
|
|
* @uses WidgetParent
|
|
*/
|
|
TabView = Y.Base.create('tabView', Y.Widget, [Y.WidgetParent], {
|
|
|
|
_afterChildAdded: function() {
|
|
this.get('contentBox').focusManager.refresh();
|
|
},
|
|
|
|
_defListNodeValueFn: function() {
|
|
var node = Y.Node.create(this.LIST_TEMPLATE);
|
|
|
|
node.addClass(Y.TabviewBase._classNames.tabviewList);
|
|
|
|
return node;
|
|
},
|
|
|
|
_defPanelNodeValueFn: function() {
|
|
var node = Y.Node.create(this.PANEL_TEMPLATE);
|
|
|
|
node.addClass(Y.TabviewBase._classNames.tabviewPanel);
|
|
|
|
return node;
|
|
},
|
|
|
|
_afterChildRemoved: function(e) { // update the selected tab when removed
|
|
var i = e.index,
|
|
selection = this.get('selection');
|
|
|
|
if (!selection) { // select previous item if selection removed
|
|
selection = this.item(i - 1) || this.item(0);
|
|
if (selection) {
|
|
selection.set('selected', 1);
|
|
}
|
|
}
|
|
|
|
this.get('contentBox').focusManager.refresh();
|
|
},
|
|
|
|
_initAria: function(contentBox) {
|
|
var tablist = contentBox.one(Y.TabviewBase._queries.tabviewList);
|
|
|
|
if (tablist) {
|
|
tablist.setAttrs({
|
|
//'aria-labelledby':
|
|
role: 'tablist'
|
|
});
|
|
}
|
|
},
|
|
|
|
bindUI: function() {
|
|
// Use the Node Focus Manager to add keyboard support:
|
|
// Pressing the left and right arrow keys will move focus
|
|
// among each of the tabs.
|
|
|
|
this.get('contentBox').plug(Y.Plugin.NodeFocusManager, {
|
|
descendants: DOT + Y.TabviewBase._classNames.tabLabel,
|
|
keys: { next: 'down:39', // Right arrow
|
|
previous: 'down:37' }, // Left arrow
|
|
circular: true
|
|
});
|
|
|
|
this.after('render', this._setDefSelection);
|
|
this.after('addChild', this._afterChildAdded);
|
|
this.after('removeChild', this._afterChildRemoved);
|
|
},
|
|
|
|
renderUI: function() {
|
|
var contentBox = this.get('contentBox');
|
|
this._renderListBox(contentBox);
|
|
this._renderPanelBox(contentBox);
|
|
this._childrenContainer = this.get('listNode');
|
|
this._renderTabs(contentBox);
|
|
this._initAria(contentBox);
|
|
},
|
|
|
|
_setDefSelection: function() {
|
|
// If no tab is selected, select the first tab.
|
|
var selection = this.get('selection') || this.item(0);
|
|
|
|
this.some(function(tab) {
|
|
if (tab.get('selected')) {
|
|
selection = tab;
|
|
return true;
|
|
}
|
|
});
|
|
if (selection) {
|
|
// TODO: why both needed? (via widgetParent/Child)?
|
|
this.set('selection', selection);
|
|
selection.set('selected', 1);
|
|
}
|
|
},
|
|
|
|
_renderListBox: function(contentBox) {
|
|
var node = this.get('listNode');
|
|
if (!node.inDoc()) {
|
|
contentBox.append(node);
|
|
}
|
|
},
|
|
|
|
_renderPanelBox: function(contentBox) {
|
|
var node = this.get('panelNode');
|
|
if (!node.inDoc()) {
|
|
contentBox.append(node);
|
|
}
|
|
},
|
|
|
|
_renderTabs: function(contentBox) {
|
|
var _classNames = Y.TabviewBase._classNames,
|
|
_queries = Y.TabviewBase._queries,
|
|
tabs = contentBox.all(_queries.tab),
|
|
panelNode = this.get('panelNode'),
|
|
panels = (panelNode) ? this.get('panelNode').get('children') : null,
|
|
tabview = this;
|
|
|
|
if (tabs) { // add classNames and fill in Tab fields from markup when possible
|
|
tabs.addClass(_classNames.tab);
|
|
contentBox.all(_queries.tabLabel).addClass(_classNames.tabLabel);
|
|
contentBox.all(_queries.tabPanel).addClass(_classNames.tabPanel);
|
|
|
|
tabs.each(function(node, i) {
|
|
var panelNode = (panels) ? panels.item(i) : null;
|
|
tabview.add({
|
|
boundingBox: node,
|
|
contentBox: node.one(DOT + _classNames.tabLabel),
|
|
panelNode: panelNode
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}, {
|
|
ATTRS: {
|
|
defaultChildType: {
|
|
value: 'Tab'
|
|
},
|
|
|
|
listNode: {
|
|
setter: function(node) {
|
|
node = Y.one(node);
|
|
if (node) {
|
|
node.addClass(Y.TabviewBase._classNames.tabviewList);
|
|
}
|
|
return node;
|
|
},
|
|
|
|
valueFn: '_defListNodeValueFn'
|
|
},
|
|
|
|
panelNode: {
|
|
setter: function(node) {
|
|
node = Y.one(node);
|
|
if (node) {
|
|
node.addClass(Y.TabviewBase._classNames.tabviewPanel);
|
|
}
|
|
return node;
|
|
},
|
|
|
|
valueFn: '_defPanelNodeValueFn'
|
|
},
|
|
|
|
tabIndex: {
|
|
value: null
|
|
//validator: '_validTabIndex'
|
|
}
|
|
},
|
|
|
|
HTML_PARSER: {
|
|
listNode: function(srcNode) {
|
|
return srcNode.one(Y.TabviewBase._queries.tabviewList);
|
|
},
|
|
panelNode: function(srcNode) {
|
|
return srcNode.one(Y.TabviewBase._queries.tabviewPanel);
|
|
}
|
|
},
|
|
|
|
// Static for legacy support.
|
|
LIST_TEMPLATE: '<ul></ul>',
|
|
PANEL_TEMPLATE: '<div></div>'
|
|
});
|
|
|
|
// Map to static values by default.
|
|
TabView.prototype.LIST_TEMPLATE = TabView.LIST_TEMPLATE;
|
|
TabView.prototype.PANEL_TEMPLATE = TabView.PANEL_TEMPLATE;
|
|
|
|
Y.TabView = TabView;
|
|
/**
|
|
* Provides Tab instances for use with TabView
|
|
* @param config {Object} Object literal specifying tabview configuration properties.
|
|
*
|
|
* @class Tab
|
|
* @constructor
|
|
* @extends Widget
|
|
* @uses WidgetChild
|
|
*/
|
|
Y.Tab = Y.Base.create('tab', Y.Widget, [Y.WidgetChild], {
|
|
BOUNDING_TEMPLATE: '<li></li>',
|
|
CONTENT_TEMPLATE: '<a></a>',
|
|
PANEL_TEMPLATE: '<div></div>',
|
|
|
|
_uiSetSelectedPanel: function(selected) {
|
|
this.get('panelNode').toggleClass(Y.TabviewBase._classNames.selectedPanel, selected);
|
|
},
|
|
|
|
_afterTabSelectedChange: function(event) {
|
|
this._uiSetSelectedPanel(event.newVal);
|
|
},
|
|
|
|
_afterParentChange: function(e) {
|
|
if (!e.newVal) {
|
|
this._remove();
|
|
} else {
|
|
this._add();
|
|
}
|
|
},
|
|
|
|
_initAria: function() {
|
|
var anchor = this.get('contentBox'),
|
|
id = anchor.get('id'),
|
|
panel = this.get('panelNode');
|
|
|
|
if (!id) {
|
|
id = Y.guid();
|
|
anchor.set('id', id);
|
|
}
|
|
// Apply the ARIA roles, states and properties to each tab
|
|
anchor.set('role', 'tab');
|
|
anchor.get('parentNode').set('role', 'presentation');
|
|
|
|
// Apply the ARIA roles, states and properties to each panel
|
|
panel.setAttrs({
|
|
role: 'tabpanel',
|
|
'aria-labelledby': id
|
|
});
|
|
},
|
|
|
|
syncUI: function() {
|
|
var _classNames = Y.TabviewBase._classNames;
|
|
|
|
this.get('boundingBox').addClass(_classNames.tab);
|
|
this.get('contentBox').addClass(_classNames.tabLabel);
|
|
this.set('label', this.get('label'));
|
|
this.set('content', this.get('content'));
|
|
this._uiSetSelectedPanel(this.get('selected'));
|
|
},
|
|
|
|
bindUI: function() {
|
|
this.after('selectedChange', this._afterTabSelectedChange);
|
|
this.after('parentChange', this._afterParentChange);
|
|
},
|
|
|
|
renderUI: function() {
|
|
this._renderPanel();
|
|
this._initAria();
|
|
},
|
|
|
|
_renderPanel: function() {
|
|
this.get('parent').get('panelNode')
|
|
.appendChild(this.get('panelNode'));
|
|
},
|
|
|
|
_add: function() {
|
|
var parent = this.get('parent').get('contentBox'),
|
|
list = parent.get('listNode'),
|
|
panel = parent.get('panelNode');
|
|
|
|
if (list) {
|
|
list.appendChild(this.get('boundingBox'));
|
|
}
|
|
|
|
if (panel) {
|
|
panel.appendChild(this.get('panelNode'));
|
|
}
|
|
},
|
|
|
|
_remove: function() {
|
|
this.get('boundingBox').remove();
|
|
this.get('panelNode').remove();
|
|
},
|
|
|
|
_onActivate: function(e) {
|
|
if (e.target === this) {
|
|
// Prevent the browser from navigating to the URL specified by the
|
|
// anchor's href attribute.
|
|
e.domEvent.preventDefault();
|
|
e.target.set('selected', 1);
|
|
}
|
|
},
|
|
|
|
initializer: function() {
|
|
this.publish(this.get('triggerEvent'), {
|
|
defaultFn: this._onActivate
|
|
});
|
|
},
|
|
|
|
_defLabelGetter: function() {
|
|
return this.get('contentBox').getHTML();
|
|
},
|
|
|
|
_defLabelSetter: function(label) {
|
|
var labelNode = this.get('contentBox');
|
|
if (labelNode.getHTML() !== label) { // Avoid rewriting existing label.
|
|
labelNode.setHTML(label);
|
|
}
|
|
return label;
|
|
},
|
|
|
|
_defContentSetter: function(content) {
|
|
var panel = this.get('panelNode');
|
|
if (panel.getHTML() !== content) { // Avoid rewriting existing content.
|
|
panel.setHTML(content);
|
|
}
|
|
return content;
|
|
},
|
|
|
|
_defContentGetter: function() {
|
|
return this.get('panelNode').getHTML();
|
|
},
|
|
|
|
// find panel by ID mapping from label href
|
|
_defPanelNodeValueFn: function() {
|
|
var _classNames = Y.TabviewBase._classNames,
|
|
href = this.get('contentBox').get('href') || '',
|
|
parent = this.get('parent'),
|
|
hashIndex = href.indexOf('#'),
|
|
panel;
|
|
|
|
href = href.substr(hashIndex);
|
|
|
|
if (href.charAt(0) === '#') { // in-page nav, find by ID
|
|
panel = Y.one(href);
|
|
if (panel) {
|
|
panel.addClass(_classNames.tabPanel);
|
|
}
|
|
}
|
|
|
|
// use the one found by id, or else try matching indices
|
|
if (!panel && parent) {
|
|
panel = parent.get('panelNode')
|
|
.get('children').item(this.get('index'));
|
|
}
|
|
|
|
if (!panel) { // create if none found
|
|
panel = Y.Node.create(this.PANEL_TEMPLATE);
|
|
panel.addClass(_classNames.tabPanel);
|
|
}
|
|
return panel;
|
|
}
|
|
}, {
|
|
ATTRS: {
|
|
/**
|
|
* @attribute triggerEvent
|
|
* @default "click"
|
|
* @type String
|
|
*/
|
|
triggerEvent: {
|
|
value: 'click'
|
|
},
|
|
|
|
/**
|
|
* @attribute label
|
|
* @type HTML
|
|
*/
|
|
label: {
|
|
setter: '_defLabelSetter',
|
|
getter: '_defLabelGetter'
|
|
},
|
|
|
|
/**
|
|
* @attribute content
|
|
* @type HTML
|
|
*/
|
|
content: {
|
|
setter: '_defContentSetter',
|
|
getter: '_defContentGetter'
|
|
},
|
|
|
|
/**
|
|
* @attribute panelNode
|
|
* @type Y.Node
|
|
*/
|
|
panelNode: {
|
|
setter: function(node) {
|
|
node = Y.one(node);
|
|
if (node) {
|
|
node.addClass(Y.TabviewBase._classNames.tabPanel);
|
|
}
|
|
return node;
|
|
},
|
|
valueFn: '_defPanelNodeValueFn'
|
|
},
|
|
|
|
tabIndex: {
|
|
value: null,
|
|
validator: '_validTabIndex'
|
|
}
|
|
|
|
},
|
|
|
|
HTML_PARSER: {
|
|
selected: function() {
|
|
var ret = (this.get('boundingBox').hasClass(Y.TabviewBase._classNames.selectedTab)) ?
|
|
1 : 0;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
|
|
}, '3.17.2', {
|
|
"requires": [
|
|
"widget",
|
|
"widget-parent",
|
|
"widget-child",
|
|
"tabview-base",
|
|
"node-pluginhost",
|
|
"node-focusmanager"
|
|
],
|
|
"skinnable": true
|
|
});
|
|
|