devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
501 lines (416 loc) • 15.8 kB
JavaScript
"use strict";
var $ = require("../core/renderer"),
eventsEngine = require("../events/core/events_engine"),
devices = require("../core/devices"),
registerComponent = require("../core/component_registrator"),
Button = require("./button"),
inkRipple = require("./widget/utils.ink_ripple"),
eventUtils = require("../events/utils"),
extend = require("../core/utils/extend").extend,
isPlainObject = require("../core/utils/type").isPlainObject,
pointerEvents = require("../events/pointer"),
TabsItem = require("./tabs/item"),
themes = require("./themes"),
holdEvent = require("../events/hold"),
Scrollable = require("./scroll_view/ui.scrollable"),
CollectionWidget = require("./collection/ui.collection_widget.edit"),
iconUtils = require("../core/utils/icon"),
BindableTemplate = require("./widget/bindable_template");
var TABS_CLASS = "dx-tabs",
TABS_WRAPPER_CLASS = "dx-tabs-wrapper",
TABS_EXPANDED_CLASS = "dx-tabs-expanded",
TABS_SCROLLABLE_CLASS = "dx-tabs-scrollable",
TABS_NAV_BUTTONS_CLASS = "dx-tabs-nav-buttons",
OVERFLOW_HIDDEN_CLASS = "dx-overflow-hidden",
TABS_ITEM_CLASS = "dx-tab",
TABS_ITEM_SELECTED_CLASS = "dx-tab-selected",
TABS_NAV_BUTTON_CLASS = "dx-tabs-nav-button",
TABS_LEFT_NAV_BUTTON_CLASS = "dx-tabs-nav-button-left",
TABS_RIGHT_NAV_BUTTON_CLASS = "dx-tabs-nav-button-right",
TABS_ITEM_TEXT_CLASS = "dx-tab-text",
TABS_ITEM_DATA_KEY = "dxTabData",
FEEDBACK_HIDE_TIMEOUT = 100,
FEEDBACK_DURATION_INTERVAL = 5,
FEEDBACK_SCROLL_TIMEOUT = 300,
TAB_OFFSET = 30;
/**
* @name dxTabs
* @publicName dxTabs
* @inherits CollectionWidget
* @module ui/tabs
* @export default
*/
var Tabs = CollectionWidget.inherit({
_activeStateUnit: "." + TABS_ITEM_CLASS,
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxTabsOptions.hoverStateEnabled
* @publicName hoverStateEnabled
* @type boolean
* @default true
* @inheritdoc
*/
hoverStateEnabled: true,
/**
* @name dxTabsOptions.showNavButtons
* @publicName showNavButtons
* @type boolean
* @default true
*/
showNavButtons: true,
/**
* @name dxTabsOptions.scrollByContent
* @publicName scrollByContent
* @type boolean
* @default true
*/
scrollByContent: true,
/**
* @name dxTabsOptions.scrollingEnabled
* @publicName scrollingEnabled
* @type boolean
* @default true
*/
scrollingEnabled: true,
/**
* @name dxTabsOptions.selectionMode
* @publicName selectionMode
* @type Enums.NavSelectionMode
* @default 'single'
*/
selectionMode: "single",
/**
* @name dxTabsOptions.activeStateEnabled
* @publicName activeStateEnabled
* @hidden
* @inheritdoc
*/
/**
* @name dxTabsOptions.noDataText
* @publicName noDataText
* @hidden
* @inheritdoc
*/
/**
* @name dxTabsOptions.selectedItems
* @publicName selectedItems
* @type Array<string,number,Object>
*/
/**
* @name dxTabsOptions.activeStateEnabled
* @publicName activeStateEnabled
* @default true
* @inheritdoc
*/
activeStateEnabled: true,
selectionRequired: false,
selectOnFocus: true,
loopItemFocus: false,
useInkRipple: false,
badgeExpr: function badgeExpr(data) {
return data ? data.badge : undefined;
}
/**
* @name dxTabsItemTemplate
* @publicName dxTabsItemTemplate
* @inherits CollectionWidgetItemTemplate
* @type object
*/
/**
* @name dxTabsItemTemplate.icon
* @publicName icon
* @type String
*/
/**
* @name dxTabsItemTemplate.badge
* @publicName badge
* @type String
*/
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
return this.callBase().concat([{
device: function device() {
return devices.real().platform !== "generic";
},
options: {
/**
* @name dxTabsOptions.showNavButtons
* @publicName showNavButtons
* @default false @for mobile_devices
*/
showNavButtons: false
}
}, {
device: { platform: "generic" },
options: {
/**
* @name dxTabsOptions.scrollByContent
* @publicName scrollByContent
* @default false @for desktop
*/
scrollByContent: false
}
}, {
device: function device() {
return devices.real().deviceType === "desktop" && !devices.isSimulator();
},
options: {
/**
* @name dxTabsOptions.focusStateEnabled
* @publicName focusStateEnabled
* @type boolean
* @default true @for desktop
* @inheritdoc
*/
focusStateEnabled: true
}
}, {
device: function device() {
return (/android5/.test(themes.current())
);
},
options: {
useInkRipple: true
}
}, {
device: function device() {
return themes.isMaterial();
},
options: {
useInkRipple: true,
selectOnFocus: false
}
}]);
},
_init: function _init() {
this.callBase();
this.setAria("role", "tablist");
this.$element().addClass(TABS_CLASS);
this._renderMultiple();
this._feedbackHideTimeout = FEEDBACK_HIDE_TIMEOUT;
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["item"] = new BindableTemplate(function ($container, data) {
if (isPlainObject(data)) {
this._prepareDefaultItemTemplate(data, $container);
} else {
$container.text(String(data));
}
var $iconElement = iconUtils.getImageContainer(data.icon);
$container.wrapInner($("<span>").addClass(TABS_ITEM_TEXT_CLASS));
$iconElement && $iconElement.prependTo($container);
}.bind(this), ["text", "html", "icon"], this.option("integrationOptions.watchMethod"));
},
_itemClass: function _itemClass() {
return TABS_ITEM_CLASS;
},
_selectedItemClass: function _selectedItemClass() {
return TABS_ITEM_SELECTED_CLASS;
},
_itemDataKey: function _itemDataKey() {
return TABS_ITEM_DATA_KEY;
},
_initMarkup: function _initMarkup() {
this.callBase();
this._renderWrapper();
this.setAria("role", "tab", this.itemElements());
this.option("useInkRipple") && this._renderInkRipple();
this.$element().addClass(OVERFLOW_HIDDEN_CLASS);
},
_render: function _render() {
this.callBase();
this._renderScrolling();
},
_renderScrolling: function _renderScrolling() {
this.$element().removeClass(TABS_EXPANDED_CLASS);
this.$element().removeClass(OVERFLOW_HIDDEN_CLASS);
if (this._allowScrolling()) {
if (!this._scrollable) {
this._renderScrollable();
this._renderNavButtons();
}
this._scrollable.update();
this._updateNavButtonsVisibility();
if (this.option("rtlEnabled")) {
this._scrollable.scrollTo({ left: this._scrollable.scrollWidth() - this._scrollable.clientWidth() });
}
this._scrollToItem(this.option("selectedItem"));
}
if (!this._allowScrolling()) {
this._cleanScrolling();
this.$element().removeClass(TABS_NAV_BUTTONS_CLASS).addClass(TABS_EXPANDED_CLASS);
}
},
_cleanNavButtons: function _cleanNavButtons() {
if (!this._leftButton || !this._rightButton) return;
this._leftButton.$element().remove();
this._rightButton.$element().remove();
this._leftButton = null;
this._rightButton = null;
},
_cleanScrolling: function _cleanScrolling() {
if (!this._scrollable) return;
this._scrollable.$content().children("." + TABS_WRAPPER_CLASS).appendTo(this._itemContainer());
this._scrollable.$element().remove();
this._scrollable = null;
this._cleanNavButtons();
},
_renderInkRipple: function _renderInkRipple() {
this._inkRipple = inkRipple.render();
},
_toggleActiveState: function _toggleActiveState($element, value, e) {
this.callBase.apply(this, arguments);
if (!this._inkRipple) {
return;
}
var config = {
element: $element,
event: e
};
if (value) {
this._inkRipple.showWave(config);
} else {
this._inkRipple.hideWave(config);
}
},
_renderMultiple: function _renderMultiple() {
if (this.option("selectionMode") === "multiple") {
this.option("selectOnFocus", false);
}
},
_renderWrapper: function _renderWrapper() {
this.$element().wrapInner($("<div>").addClass(TABS_WRAPPER_CLASS));
},
_renderScrollable: function _renderScrollable() {
var $itemContainer = this.$element().wrapInner($("<div>").addClass(TABS_SCROLLABLE_CLASS)).children();
this._scrollable = this._createComponent($itemContainer, Scrollable, {
direction: "horizontal",
showScrollbar: false,
useKeyboard: false,
useNative: false,
scrollByContent: this.option("scrollByContent"),
onScroll: this._updateNavButtonsVisibility.bind(this)
});
this.$element().append(this._scrollable.$element());
},
_scrollToItem: function _scrollToItem(itemData) {
if (!this._scrollable) return;
var $item = this._editStrategy.getItemElement(itemData);
this._scrollable.scrollToElement($item);
},
_allowScrolling: function _allowScrolling() {
if (!this.option("scrollingEnabled")) {
return false;
}
var tabItemsWidth = 0;
this._getAvailableItems().each(function (_, tabItem) {
tabItemsWidth += $(tabItem).outerWidth(true);
});
// NOTE: "-1" is a hack fix for IE (T190044)
return tabItemsWidth - 1 > this.$element().width();
},
_renderNavButtons: function _renderNavButtons() {
this.$element().toggleClass(TABS_NAV_BUTTONS_CLASS, this.option("showNavButtons"));
if (!this.option("showNavButtons")) return;
this._leftButton = this._createNavButton(-TAB_OFFSET, "chevronprev");
var $leftButton = this._leftButton.$element();
$leftButton.addClass(TABS_LEFT_NAV_BUTTON_CLASS);
this.$element().prepend($leftButton);
this._rightButton = this._createNavButton(TAB_OFFSET, "chevronnext");
var $rightButton = this._rightButton.$element();
$rightButton.addClass(TABS_RIGHT_NAV_BUTTON_CLASS);
this.$element().append($rightButton);
},
_updateNavButtonsVisibility: function _updateNavButtonsVisibility() {
this._leftButton && this._leftButton.option("disabled", this._scrollable.scrollLeft() <= 0);
this._rightButton && this._rightButton.option("disabled", this._scrollable.scrollLeft() >= Math.round(this._scrollable.scrollWidth() - this._scrollable.clientWidth()));
},
_updateScrollPosition: function _updateScrollPosition(offset, duration) {
this._scrollable.update();
this._scrollable.scrollBy(offset / duration);
},
_createNavButton: function _createNavButton(offset, icon) {
var that = this;
var holdAction = that._createAction(function () {
that._holdInterval = setInterval(function () {
that._updateScrollPosition(offset, FEEDBACK_DURATION_INTERVAL);
}, FEEDBACK_DURATION_INTERVAL);
}),
holdEventName = eventUtils.addNamespace(holdEvent.name, "dxNavButton"),
pointerUpEventName = eventUtils.addNamespace(pointerEvents.up, "dxNavButton"),
pointerOutEventName = eventUtils.addNamespace(pointerEvents.out, "dxNavButton");
var navButton = this._createComponent($("<div>").addClass(TABS_NAV_BUTTON_CLASS), Button, {
focusStateEnabled: false,
icon: icon,
onClick: function onClick() {
that._updateScrollPosition(offset, 1);
},
integrationOptions: {}
});
var $navButton = navButton.$element();
eventsEngine.on($navButton, holdEventName, { timeout: FEEDBACK_SCROLL_TIMEOUT }, function (e) {
holdAction({ event: e });
}.bind(this));
eventsEngine.on($navButton, pointerUpEventName, function () {
that._clearInterval();
});
eventsEngine.on($navButton, pointerOutEventName, function () {
that._clearInterval();
});
return navButton;
},
_clearInterval: function _clearInterval() {
if (this._holdInterval) clearInterval(this._holdInterval);
},
_renderSelection: function _renderSelection(addedSelection) {
this._scrollable && this._scrollable.scrollToElement(this.itemElements().eq(addedSelection[0]), { left: 1, right: 1 });
},
_visibilityChanged: function _visibilityChanged(visible) {
if (visible) {
this._dimensionChanged();
}
},
_dimensionChanged: function _dimensionChanged() {
if (this.option("scrollingEnabled")) {
this._renderScrolling();
}
},
_itemSelectHandler: function _itemSelectHandler(e) {
if (this.option("selectionMode") === "single" && this.isItemSelected(e.currentTarget)) {
return;
}
this.callBase(e);
},
_clean: function _clean() {
this._scrollable = null;
this.callBase();
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "useInkRipple":
case "scrollingEnabled":
case "showNavButtons":
this._invalidate();
break;
case "scrollByContent":
this._scrollable && this._scrollable.option(args.name, args.value);
break;
case "selectionMode":
this._renderMultiple();
this.callBase(args);
break;
case "badgeExpr":
this._invalidate();
break;
default:
this.callBase(args);
}
}
});
Tabs.ItemClass = TabsItem;
registerComponent("dxTabs", Tabs);
module.exports = Tabs;
module.exports.getTabsExpandedClass = TABS_EXPANDED_CLASS;