UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

358 lines (357 loc) • 13.1 kB
/** * DevExtreme (ui/pivot.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var $ = require("../core/renderer"), eventsEngine = require("../events/core/events_engine"), fx = require("../animation/fx"), swipeEvents = require("../events/swipe"), translator = require("../animation/translator"), domUtils = require("../core/utils/dom"), extend = require("../core/utils/extend").extend, isDefined = require("../core/utils/type").isDefined, registerComponent = require("../core/component_registrator"), eventUtils = require("../events/utils"), config = require("../core/config"), CollectionWidget = require("./collection/ui.collection_widget.edit"), PivotTabs = require("./pivot/ui.pivot_tabs"), EmptyTemplate = require("./widget/empty_template"), ChildDefaultTemplate = require("./widget/child_default_template"), Deferred = require("../core/utils/deferred").Deferred; var PIVOT_CLASS = "dx-pivot", PIVOT_AUTOHEIGHT_CLASS = "dx-pivot-autoheight", PIVOT_WRAPPER_CLASS = "dx-pivot-wrapper", PIVOT_TABS_CONTAINER_CLASS = "dx-pivottabs-container", PIVOT_ITEM_CONTAINER_CLASS = "dx-pivot-itemcontainer", PIVOT_ITEM_WRAPPER_CLASS = "dx-pivot-itemwrapper", PIVOT_ITEM_CLASS = "dx-pivot-item", PIVOT_ITEM_HIDDEN_CLASS = "dx-pivot-item-hidden", PIVOT_ITEM_DATA_KEY = "dxPivotItemData", PIVOT_RETURN_BACK_DURATION = 200, PIVOT_SLIDE_AWAY_DURATION = 50, PIVOT_SLIDE_BACK_DURATION = 250, PIVOT_SLIDE_BACK_EASING = "cubic-bezier(.10, 1, 0, 1)"; var animation = { returnBack: function($element) { fx.animate($element, { type: "slide", to: { left: 0 }, duration: PIVOT_RETURN_BACK_DURATION }) }, slideAway: function($element, position, complete) { fx.animate($element, { type: "slide", to: { left: position }, duration: PIVOT_SLIDE_AWAY_DURATION, complete: complete }) }, slideBack: function($element) { fx.animate($element, { type: "slide", to: { left: 0 }, easing: PIVOT_SLIDE_BACK_EASING, duration: PIVOT_SLIDE_BACK_DURATION }) }, complete: function($element) { fx.stop($element, true) } }; var Pivot = CollectionWidget.inherit({ _getDefaultOptions: function() { return extend(this.callBase(), { selectedIndex: 0, swipeEnabled: true, itemTitleTemplate: "title", contentTemplate: "content", focusStateEnabled: false, selectionMode: "single", selectionRequired: true, selectionByClick: false }) }, _itemClass: function() { return PIVOT_ITEM_CLASS }, _itemDataKey: function() { return PIVOT_ITEM_DATA_KEY }, _itemContainer: function() { return this._$itemWrapper }, _elementWidth: function() { if (!this._elementWidthCache) { this._elementWidthCache = this.$element().width() } return this._elementWidthCache }, _clearElementWidthCache: function() { delete this._elementWidthCache }, _init: function() { this.callBase(); this.$element().addClass(PIVOT_CLASS); this._initWrapper(); this._initTabs(); this._initItemContainer(); this._clearItemsCache(); this._initSwipeHandlers() }, _initTemplates: function() { this.callBase(); this._defaultTemplates.content = new EmptyTemplate; this._defaultTemplates.title = new ChildDefaultTemplate("item", this) }, _dimensionChanged: function() { this._clearElementWidthCache() }, _initWrapper: function() { this._$wrapper = $("<div>").addClass(PIVOT_WRAPPER_CLASS).appendTo(this.$element()) }, _initItemContainer: function() { var $itemContainer = $("<div>").addClass(PIVOT_ITEM_CONTAINER_CLASS); this._$wrapper.append($itemContainer); this._$itemWrapper = $("<div>").addClass(PIVOT_ITEM_WRAPPER_CLASS); $itemContainer.append(this._$itemWrapper) }, _clearItemsCache: function() { this._itemsCache = [] }, _initTabs: function() { var that = this, $tabsContainer = $("<div>").addClass(PIVOT_TABS_CONTAINER_CLASS); this._$wrapper.append($tabsContainer); this._tabs = this._createComponent($tabsContainer, PivotTabs, { itemTemplateProperty: "titleTemplate", itemTemplate: this._getTemplateByOption("itemTitleTemplate"), items: this.option("items"), selectedIndex: this.option("selectedIndex"), onPrepare: function() { that._prepareAnimation() }, onUpdatePosition: function(args) { that._updateContentPosition(args.offset) }, onRollback: function() { that._animateRollback() }, onSelectionChanged: function(args) { that.option("selectedItem", args.addedItems[0]) }, swipeEnabled: this.option("swipeEnabled") }) }, _initMarkup: function() { this._renderContentTemplate(); this.callBase() }, _render: function() { this.callBase(); var selectedIndex = this.option("selectedIndex"); this._renderCurrentContent(selectedIndex, selectedIndex) }, _renderContentTemplate: function() { if (isDefined(this._singleContent)) { return } this._getTemplateByOption("contentTemplate").render({ container: domUtils.getPublicElement(this._$itemWrapper) }); this._singleContent = !this._$itemWrapper.is(":empty") }, _renderDimensions: function() { this.callBase(); this.$element().toggleClass(PIVOT_AUTOHEIGHT_CLASS, "auto" === this.option("height")) }, _visibilityChanged: function(visible) { if (visible) { this._tabs._dimensionChanged() } }, _renderCurrentContent: function(currentIndex, previousIndex) { var itemsCache = this._itemsCache; itemsCache[previousIndex] = this._selectedItemElement(); var $hidingItem = itemsCache[previousIndex], $showingItem = itemsCache[currentIndex]; domUtils.triggerHidingEvent($hidingItem); $hidingItem.addClass(PIVOT_ITEM_HIDDEN_CLASS); if ($showingItem) { $showingItem.removeClass(PIVOT_ITEM_HIDDEN_CLASS); domUtils.triggerShownEvent($showingItem) } else { this._prepareContent(); this._renderContent() } this._selectionChangePromise && this._selectionChangePromise.resolve(); this._selectionChangePromise = new Deferred }, _updateContentPosition: function(offset) { translator.move(this._$itemWrapper, { left: this._calculatePixelOffset(offset) }) }, _animateRollback: function() { animation.returnBack(this._$itemWrapper) }, _animateComplete: function(newIndex, currentIndex) { var $itemWrapper = this._$itemWrapper, rtlSignCorrection = this._getRTLSignCorrection(), intermediatePosition = this._elementWidth() * (this._isPrevSwipeHandled() ? 1 : -1) * rtlSignCorrection; animation.slideAway($itemWrapper, intermediatePosition, function() { translator.move($itemWrapper, { left: -intermediatePosition }); this._renderCurrentContent(newIndex, currentIndex) }.bind(this)); animation.slideBack($itemWrapper) }, _calculatePixelOffset: function(offset) { offset = offset || 0; return offset * this._elementWidth() }, _isPrevSwipeHandled: function() { var wrapperOffset = translator.locate(this._$itemWrapper).left, rtl = this.option("rtlEnabled"); return rtl ^ wrapperOffset > 0 && 0 !== wrapperOffset }, _initSwipeHandlers: function() { var $element = this.$element(); eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.start, this.NAME), { itemSizeFunc: this._elementWidth.bind(this) }, this._swipeStartHandler.bind(this)); eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.swipe, this.NAME), this._swipeUpdateHandler.bind(this)); eventsEngine.on($element, eventUtils.addNamespace(swipeEvents.end, this.NAME), this._swipeEndHandler.bind(this)) }, _swipeStartHandler: function(e) { this._prepareAnimation(); this._tabs.prepare(); if (config().designMode || this.option("disabled") || !this.option("swipeEnabled") || this._indexBoundary() <= 1) { e.cancel = true } else { this._swipeGestureRunning = true } e.maxLeftOffset = 1; e.maxRightOffset = 1 }, _prepareAnimation: function() { this._stopAnimation() }, _stopAnimation: function() { animation.complete(this._$itemWrapper) }, _swipeUpdateHandler: function(e) { var offset = e.offset; this._updateContentPosition(offset); this._tabs.updatePosition(offset) }, _swipeEndHandler: function(e) { var targetOffset = e.targetOffset * this._getRTLSignCorrection(); if (0 === targetOffset) { this._animateRollback(); this._tabs.rollback() } else { var newIndex = this._normalizeIndex(this.option("selectedIndex") - targetOffset); this.option("selectedIndex", newIndex) } this._swipeGestureRunning = false }, _normalizeIndex: function(index) { var boundary = this._indexBoundary(); if (index < 0) { index = boundary + index } if (index >= boundary) { index -= boundary } return index }, _indexBoundary: function() { return this.option("items").length }, _renderContentImpl: function() { if (this._singleContent) { return } var items = this.option("items"), selectedIndex = this.option("selectedIndex"); if (items.length) { this._renderItems([items[selectedIndex]]) } }, _selectedItemElement: function() { return this._$itemWrapper.children("." + PIVOT_ITEM_CLASS + ":not(." + PIVOT_ITEM_HIDDEN_CLASS + ")") }, _getRTLSignCorrection: function() { return this.option("rtlEnabled") ? -1 : 1 }, _clean: function() { animation.complete(this._$itemWrapper); this.callBase() }, _cleanItemContainer: function() { if (this._singleContent) { return } this.callBase() }, _refresh: function() { this._tabs._refresh(); this.callBase() }, _updateSelection: function(addedItems, removedItems) { var newIndex = addedItems[0], oldIndex = removedItems[0]; if (!this._swipeGestureRunning) { this._prepareAnimation() } this._animateComplete(newIndex, oldIndex); this._tabs.option("selectedIndex", newIndex) }, _optionChanged: function(args) { var value = args.value; switch (args.name) { case "disabled": this._tabs.option("disabled", value); this.callBase(args); break; case "items": this._tabs.option(args.fullName, value); this._clearItemsCache(); this.callBase(args); break; case "rtlEnabled": this._tabs.option("rtlEnabled", value); this._clearItemsCache(); this.callBase(args); break; case "itemTitleTemplate": this._tabs.option("itemTemplate", this._getTemplate(value)); break; case "swipeEnabled": this._tabs.option("swipeEnabled", value); break; case "contentTemplate": this._singleContent = null; this._invalidate(); break; default: this.callBase(args) } } }); registerComponent("dxPivot", Pivot); module.exports = Pivot; module.exports.default = module.exports;