UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

478 lines (379 loc) • 14.6 kB
"use strict"; var $ = require("../core/renderer"), fx = require("../animation/fx"), translator = require("../animation/translator"), mathUtils = require("../core/utils/math"), extend = require("../core/utils/extend").extend, noop = require("../core/utils/common").noop, isDefined = require("../core/utils/type").isDefined, devices = require("../core/devices"), getPublicElement = require("../core/utils/dom").getPublicElement, registerComponent = require("../core/component_registrator"), CollectionWidget = require("./collection/ui.collection_widget.edit"), Swipeable = require("../events/gesture/swipeable"), Deferred = require("../core/utils/deferred").Deferred; var MULTIVIEW_CLASS = "dx-multiview", MULTIVIEW_WRAPPER_CLASS = "dx-multiview-wrapper", MULTIVIEW_ITEM_CONTAINER_CLASS = "dx-multiview-item-container", MULTIVIEW_ITEM_CLASS = "dx-multiview-item", MULTIVIEW_ITEM_HIDDEN_CLASS = "dx-multiview-item-hidden", MULTIVIEW_ITEM_DATA_KEY = "dxMultiViewItemData", MULTIVIEW_ANIMATION_DURATION = 200; var toNumber = function toNumber(value) { return +value; }; var position = function position($element) { return translator.locate($element).left; }; var move = function move($element, position) { translator.move($element, { left: position }); }; var animation = { moveTo: function moveTo($element, position, duration, completeAction) { fx.animate($element, { type: "slide", to: { left: position }, duration: duration, complete: completeAction }); }, complete: function complete($element) { fx.stop($element, true); } }; /** * @name dxMultiView * @publicName dxMultiView * @inherits CollectionWidget * @module ui/multi_view * @export default */ var MultiView = CollectionWidget.inherit({ _activeStateUnit: "." + MULTIVIEW_ITEM_CLASS, _supportedKeys: function _supportedKeys() { return extend(this.callBase(), { pageUp: noop, pageDown: noop }); }, _getDefaultOptions: function _getDefaultOptions() { return extend(this.callBase(), { /** * @name dxMultiViewOptions.selectedIndex * @publicName selectedIndex * @type number * @default 0 */ selectedIndex: 0, /** * @name dxMultiViewOptions.swipeenabled * @publicName swipeEnabled * @type boolean * @default true */ swipeEnabled: true, /** * @name dxMultiViewOptions.animationenabled * @publicName animationEnabled * @type boolean * @default true */ animationEnabled: true, /** * @name dxMultiViewOptions.loop * @publicName loop * @type boolean * @default false */ loop: false, /** * @name dxMultiViewOptions.deferRendering * @publicName deferRendering * @type boolean * @default true */ deferRendering: true, /** * @name dxMultiViewOptions.selectedItems * @publicName selectedItems * @hidden * @inheritdoc */ /** * @name dxMultiViewOptions.selectedItemKeys * @publicName selectedItemKeys * @hidden * @inheritdoc */ /** * @name dxMultiViewOptions.keyExpr * @publicName keyExpr * @hidden * @inheritdoc */ _itemAttributes: { role: "tabpanel" }, loopItemFocus: false, selectOnFocus: true, selectionMode: "single", selectionRequired: true, selectionByClick: false }); }, _defaultOptionsRules: function _defaultOptionsRules() { return this.callBase().concat([{ device: function device() { return devices.real().deviceType === "desktop" && !devices.isSimulator(); }, options: { /** * @name dxMultiViewOptions.focusStateEnabled * @publicName focusStateEnabled * @type boolean * @default true @for desktop * @inheritdoc */ focusStateEnabled: true } }]); }, _itemClass: function _itemClass() { return MULTIVIEW_ITEM_CLASS; }, _itemDataKey: function _itemDataKey() { return MULTIVIEW_ITEM_DATA_KEY; }, _itemContainer: function _itemContainer() { return this._$itemContainer; }, _itemElements: function _itemElements() { return this._itemContainer().children(this._itemSelector()); }, _itemWidth: function _itemWidth() { if (!this._itemWidthValue) { this._itemWidthValue = this._$wrapper.width(); } return this._itemWidthValue; }, _clearItemWidthCache: function _clearItemWidthCache() { delete this._itemWidthValue; }, _itemsCount: function _itemsCount() { return this.option("items").length; }, _normalizeIndex: function _normalizeIndex(index) { var count = this._itemsCount(); if (index < 0) { index = index + count; } if (index >= count) { index = index - count; } return index; }, _getRTLSignCorrection: function _getRTLSignCorrection() { return this.option("rtlEnabled") ? -1 : 1; }, _init: function _init() { this.callBase.apply(this, arguments); var $element = this.$element(); $element.addClass(MULTIVIEW_CLASS); this._$wrapper = $("<div>").addClass(MULTIVIEW_WRAPPER_CLASS); this._$wrapper.appendTo($element); this._$itemContainer = $("<div>").addClass(MULTIVIEW_ITEM_CONTAINER_CLASS); this._$itemContainer.appendTo(this._$wrapper); this.option("loopItemFocus", this.option("loop")); this._initSwipeable(); }, _initMarkup: function _initMarkup() { this._deferredItems = []; this.callBase(); }, _renderItemContent: function _renderItemContent(args) { var renderContentDeferred = new Deferred(); var that = this, callBase = this.callBase; var deferred = new Deferred(); deferred.done(function () { var $itemContent = callBase.call(that, args); renderContentDeferred.resolve($itemContent); }); this._deferredItems[args.index] = deferred; this.option("deferRendering") || deferred.resolve(); return renderContentDeferred.promise(); }, _render: function _render() { this.callBase(); var selectedItemIndices = this._getSelectedItemIndices(); this._updateItemsPosition(selectedItemIndices[0]); this._updateItemsVisibility(selectedItemIndices[0]); }, _renderSelection: function _renderSelection(addedSelection) { this._updateItemsVisibility(addedSelection[0]); }, _updateItems: function _updateItems(selectedIndex, newIndex) { this._updateItemsPosition(selectedIndex, newIndex); this._updateItemsVisibility(selectedIndex, newIndex); }, _updateItemsPosition: function _updateItemsPosition(selectedIndex, newIndex) { var $itemElements = this._itemElements(), positionSign = -this._animationDirection(newIndex, selectedIndex), $selectedItem = $itemElements.eq(selectedIndex); move($selectedItem, 0); move($itemElements.eq(newIndex), positionSign * 100 + "%"); }, _updateItemsVisibility: function _updateItemsVisibility(selectedIndex, newIndex) { var $itemElements = this._itemElements(); $itemElements.each(function (itemIndex, item) { var $item = $(item), isHidden = itemIndex !== selectedIndex && itemIndex !== newIndex; if (!isHidden) { this._renderSpecificItem(itemIndex); } $item.toggleClass(MULTIVIEW_ITEM_HIDDEN_CLASS, isHidden); this.setAria("hidden", isHidden || undefined, $item); }.bind(this)); }, _renderSpecificItem: function _renderSpecificItem(index) { var hasItemContent = this._itemElements().eq(index).find(this._itemContentClass()).length > 0; if (isDefined(index) && !hasItemContent) { this._deferredItems[index].resolve(); } }, _refreshItem: function _refreshItem($item, item) { this.callBase($item, item); this._updateItemsVisibility(this.option("selectedIndex")); }, _setAriaSelected: noop, _updateSelection: function _updateSelection(addedSelection, removedSelection) { var newIndex = addedSelection[0], prevIndex = removedSelection[0]; animation.complete(this._$itemContainer); this._updateItems(prevIndex, newIndex); var animationDirection = this._animationDirection(newIndex, prevIndex); this._animateItemContainer(animationDirection * this._itemWidth(), function () { move(this._$itemContainer, 0); this._updateItems(newIndex); // NOTE: force layout recalculation on iOS 6 & iOS 7.0 (B254713) this._$itemContainer.width(); }.bind(this)); }, _animateItemContainer: function _animateItemContainer(position, completeCallback) { var duration = this.option("animationEnabled") ? MULTIVIEW_ANIMATION_DURATION : 0; animation.moveTo(this._$itemContainer, position, duration, completeCallback); }, _animationDirection: function _animationDirection(newIndex, prevIndex) { var containerPosition = position(this._$itemContainer), indexDifference = (prevIndex - newIndex) * this._getRTLSignCorrection() * this._getItemFocusLoopSignCorrection(), isSwipePresent = containerPosition !== 0, directionSignVariable = isSwipePresent ? containerPosition : indexDifference; return mathUtils.sign(directionSignVariable); }, _initSwipeable: function _initSwipeable() { this._createComponent(this.$element(), Swipeable, { disabled: !this.option("swipeEnabled"), elastic: false, itemSizeFunc: this._itemWidth.bind(this), onStart: function (args) { this._swipeStartHandler(args.event); }.bind(this), onUpdated: function (args) { this._swipeUpdateHandler(args.event); }.bind(this), onEnd: function (args) { this._swipeEndHandler(args.event); }.bind(this) }); }, _swipeStartHandler: function _swipeStartHandler(e) { animation.complete(this._$itemContainer); var selectedIndex = this.option("selectedIndex"), loop = this.option("loop"), lastIndex = this._itemsCount() - 1, rtl = this.option("rtlEnabled"); e.maxLeftOffset = toNumber(loop || (rtl ? selectedIndex > 0 : selectedIndex < lastIndex)); e.maxRightOffset = toNumber(loop || (rtl ? selectedIndex < lastIndex : selectedIndex > 0)); this._swipeDirection = null; }, _swipeUpdateHandler: function _swipeUpdateHandler(e) { var offset = e.offset, swipeDirection = mathUtils.sign(offset) * this._getRTLSignCorrection(); move(this._$itemContainer, offset * this._itemWidth()); if (swipeDirection !== this._swipeDirection) { this._swipeDirection = swipeDirection; var selectedIndex = this.option("selectedIndex"), newIndex = this._normalizeIndex(selectedIndex - swipeDirection); this._updateItems(selectedIndex, newIndex); } }, _swipeEndHandler: function _swipeEndHandler(e) { var targetOffset = e.targetOffset * this._getRTLSignCorrection(); if (targetOffset) { this.option("selectedIndex", this._normalizeIndex(this.option("selectedIndex") - targetOffset)); // TODO: change focusedElement on focusedItem var $selectedElement = this.itemElements().filter(".dx-item-selected"); this.option("focusStateEnabled") && this.option("focusedElement", getPublicElement($selectedElement)); } else { this._animateItemContainer(0, noop); } }, _getItemFocusLoopSignCorrection: function _getItemFocusLoopSignCorrection() { return this._itemFocusLooped ? -1 : 1; }, _moveFocus: function _moveFocus() { this.callBase.apply(this, arguments); this._itemFocusLooped = false; }, _prevItem: function _prevItem($items) { var $result = this.callBase.apply(this, arguments); this._itemFocusLooped = $result.is($items.last()); return $result; }, _nextItem: function _nextItem($items) { var $result = this.callBase.apply(this, arguments); this._itemFocusLooped = $result.is($items.first()); return $result; }, _dimensionChanged: function _dimensionChanged() { this._clearItemWidthCache(); }, _visibilityChanged: function _visibilityChanged(visible) { if (visible) { this._dimensionChanged(); } }, _optionChanged: function _optionChanged(args) { var value = args.value; switch (args.name) { case "loop": this.option("loopItemFocus", value); break; case "animationEnabled": break; case "swipeEnabled": Swipeable.getInstance(this.$element()).option("disabled", !value); break; case "deferRendering": this._invalidate(); break; default: this.callBase(args); } } }); /** * @name dxMultiViewItemTemplate * @publicName dxMultiViewItemTemplate * @inherits CollectionWidgetItemTemplate * @type object */ /** * @name dxMultiViewItemTemplate.visible * @publicName visible * @hidden * @inheritdoc */ registerComponent("dxMultiView", MultiView); module.exports = MultiView; ///#DEBUG module.exports.animation = animation; ///#ENDDEBUG