devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
708 lines (707 loc) • 28.8 kB
JavaScript
/**
* DevExtreme (ui/panorama.js)
* Version: 18.2.18
* Build date: Tue Oct 18 2022
*
* Copyright (c) 2012 - 2022 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"),
noop = require("../core/utils/common").noop,
when = require("../core/utils/deferred").when,
fx = require("../animation/fx"),
translator = require("../animation/translator"),
Class = require("../core/class"),
extend = require("../core/utils/extend").extend,
inArray = require("../core/utils/array").inArray,
each = require("../core/utils/iterator").each,
abstract = abstract,
registerComponent = require("../core/component_registrator"),
PanoramaItem = require("./panorama/item"),
swipeEvents = require("../events/swipe"),
eventUtils = require("../events/utils"),
config = require("../core/config"),
CollectionWidget = require("./collection/ui.collection_widget.edit");
var PANORAMA_CLASS = "dx-panorama",
PANORAMA_WRAPPER_CLASS = "dx-panorama-wrapper",
PANORAMA_TITLE_CLASS = "dx-panorama-title",
PANORAMA_GHOST_TITLE_CLASS = "dx-panorama-ghosttitle",
PANORAMA_ITEMS_CONTAINER_CLASS = "dx-panorama-itemscontainer",
PANORAMA_ITEM_CLASS = "dx-panorama-item",
PANORAMA_GHOST_ITEM_CLASS = "dx-panorama-ghostitem",
PANORAMA_ITEM_DATA_KEY = "dxPanoramaItemData",
PANORAMA_ITEM_MARGIN_SCALE = .02,
PANORAMA_TITLE_MARGIN_SCALE = .02,
PANORAMA_BACKGROUND_MOVE_DURATION = 300,
PANORAMA_BACKGROUND_MOVE_EASING = "cubic-bezier(.40, .80, .60, 1)",
PANORAMA_TITLE_MOVE_DURATION = 300,
PANORAMA_TITLE_MOVE_EASING = "cubic-bezier(.40, .80, .60, 1)",
PANORAMA_ITEM_MOVE_DURATION = 300,
PANORAMA_ITEM_MOVE_EASING = "cubic-bezier(.40, .80, .60, 1)";
var moveBackground = function($element, position) {
$element.css("backgroundPosition", position + "px 0%")
};
var position = function($element) {
return translator.locate($element).left
};
var move = function($element, position) {
translator.move($element, {
left: position
});
$element.css("visibility", "")
};
var animation = {
backgroundMove: function($element, position, completeAction) {
return fx.animate($element, {
to: {
backgroundPosition: position + "px 0%"
},
duration: PANORAMA_BACKGROUND_MOVE_DURATION,
easing: PANORAMA_BACKGROUND_MOVE_EASING,
complete: completeAction
})
},
titleMove: function($title, position, completeAction) {
return fx.animate($title, {
type: "slide",
to: {
left: position
},
duration: PANORAMA_TITLE_MOVE_DURATION,
easing: PANORAMA_TITLE_MOVE_EASING,
complete: completeAction
})
},
itemMove: function($item, itemPosition, completeAction) {
return fx.animate($item, {
type: "slide",
to: {
left: itemPosition
},
duration: PANORAMA_ITEM_MOVE_DURATION,
easing: PANORAMA_ITEM_MOVE_EASING,
complete: function() {
completeAction && completeAction.apply(this, arguments);
$item.css("visibility", position($item) > 0 ? "" : "hidden")
}
})
}
};
var endAnimation = function(elements) {
if (!elements) {
return
}
each(elements, function(_, element) {
fx.stop(element, true)
})
};
var PanoramaItemsRenderStrategy = Class.inherit({
ctor: function(panorama) {
this._panorama = panorama
},
init: noop,
render: noop,
allItemElements: function() {
return this._panorama._itemElements()
},
updatePositions: abstract,
animateRollback: abstract,
detectBoundsTransition: abstract,
animateComplete: abstract,
_getRTLSignCorrection: function() {
return this._panorama._getRTLSignCorrection()
},
_isRTLEnabled: function() {
return this._panorama.option("rtlEnabled")
},
_itemMargin: function() {
return this._containerWidth() * PANORAMA_ITEM_MARGIN_SCALE
},
_containerWidth: function() {
return this._panorama._elementWidth()
},
_itemWidth: function() {
return this._panorama._itemWidth()
},
_indexBoundary: function() {
return this._panorama._indexBoundary()
},
_normalizeIndex: function(index) {
return this._panorama._normalizeIndex(index)
},
_startNextPosition: function() {
if (this._isRTLEnabled()) {
return this._containerWidth() - (this._itemMargin() + this._itemWidth())
} else {
return this._itemMargin()
}
},
_startPrevPosition: function() {
if (this._isRTLEnabled()) {
return this._containerWidth()
} else {
return -this._itemWidth()
}
}
});
var PanoramaOneAndLessItemsRenderStrategy = PanoramaItemsRenderStrategy.inherit({
updatePositions: function() {
var $items = this._panorama._itemElements(),
startPosition = this._startNextPosition();
$items.each(function() {
move($(this), startPosition)
})
},
animateRollback: noop,
detectBoundsTransition: noop,
animateComplete: noop
});
var PanoramaTwoItemsRenderStrategy = PanoramaItemsRenderStrategy.inherit({
init: function() {
this._initGhostItem()
},
render: function() {
this._renderGhostItem()
},
_initGhostItem: function() {
this._$ghostItem = $("<div>").addClass(PANORAMA_GHOST_ITEM_CLASS)
},
_renderGhostItem: function() {
this._panorama._itemContainer().append(this._$ghostItem);
this._toggleGhostItem(false)
},
_toggleGhostItem: function(visible) {
var $ghostItem = this._$ghostItem;
if (visible) {
$ghostItem.css("opacity", 1)
} else {
$ghostItem.css("opacity", 0)
}
},
_updateGhostItemContent: function(index) {
if (false !== index && index !== this._prevGhostIndex) {
this._$ghostItem.html(this._panorama._itemElements().eq(index).html());
this._prevGhostIndex = index
}
},
_isGhostItemVisible: function() {
return "1" === this._$ghostItem.css("opacity")
},
_swapGhostWithItem: function($item) {
var $ghostItem = this._$ghostItem,
lastItemPosition = position($item);
move($item, position($ghostItem));
move($ghostItem, lastItemPosition)
},
allItemElements: function() {
return this._panorama._itemContainer().find("." + PANORAMA_ITEM_CLASS + ", ." + PANORAMA_GHOST_ITEM_CLASS)
},
updatePositions: function(offset) {
var $items = this.allItemElements(),
selectedIndex = this._panorama.option("selectedIndex"),
adjustedOffset = offset * this._getRTLSignCorrection(),
isGhostReplaceLast = adjustedOffset > 0 && 0 === selectedIndex || adjustedOffset < 0 && 1 === selectedIndex,
isGhostReplaceFirst = adjustedOffset < 0 && 0 === selectedIndex || adjustedOffset > 0 && 1 === selectedIndex,
ghostPosition = isGhostReplaceLast && "replaceLast" || isGhostReplaceFirst && "replaceFirst",
ghostContentIndex = isGhostReplaceLast && 1 || isGhostReplaceFirst && 0,
positions = this._calculateItemPositions(selectedIndex, ghostPosition);
this._updateGhostItemContent(ghostContentIndex);
this._toggleGhostItem(isGhostReplaceLast || isGhostReplaceFirst);
$items.each(function(index) {
move($(this), positions[index] + offset)
})
},
animateRollback: function(currentIndex) {
var that = this,
$items = this._panorama._itemElements(),
startPosition = this._startNextPosition(),
signCorrection = this._getRTLSignCorrection(),
offset = (position($items.eq(currentIndex)) - startPosition) * signCorrection,
ghostOffset = (position(this._$ghostItem) - startPosition) * signCorrection,
positions = this._calculateItemPositions(currentIndex, ghostOffset > 0 ? "prepend" : "append"),
isLastReplacedByGhost = 0 === currentIndex && offset > 0 && ghostOffset > 0 || 1 === currentIndex && ghostOffset < 0;
if (isLastReplacedByGhost) {
this._swapGhostWithItem($items.eq(1))
} else {
this._swapGhostWithItem($items.eq(0))
}
$items.each(function(index) {
animation.itemMove($(this), positions[index])
});
animation.itemMove(this._$ghostItem, positions[2], function() {
that._toggleGhostItem(false)
})
},
detectBoundsTransition: function(newIndex, currentIndex) {
var ghostLocation = position(this._$ghostItem),
startPosition = this._startNextPosition(),
rtl = this._isRTLEnabled();
if (0 === newIndex && rtl ^ ghostLocation < startPosition) {
return "left"
}
if (0 === currentIndex && rtl ^ ghostLocation > startPosition) {
return "right"
}
},
animateComplete: function(boundCross, newIndex, currentIndex) {
var that = this,
ghostPosition = !boundCross ^ 0 !== currentIndex ? "prepend" : "append",
$items = this._panorama._itemElements(),
positions = this._calculateItemPositions(newIndex, ghostPosition),
animations = [];
$items.each(function(index) {
animations.push(animation.itemMove($(this), positions[index]))
});
animations.push(animation.itemMove(this._$ghostItem, positions[2], function() {
that._toggleGhostItem(false)
}));
return when.apply($, animations)
},
_calculateItemPositions: function(atIndex, ghostPosition) {
var positions = [],
itemMargin = this._itemMargin(),
itemWidth = this._itemWidth(),
itemPositionOffset = (itemWidth + itemMargin) * this._getRTLSignCorrection(),
normalFlow = 0 === atIndex,
prevPosition = this._startPrevPosition(),
nextPosition = this._startNextPosition();
positions.push(nextPosition);
nextPosition += itemPositionOffset;
if (normalFlow) {
positions.push(nextPosition)
} else {
positions.splice(0, 0, nextPosition)
}
nextPosition += itemPositionOffset;
switch (ghostPosition) {
case "replaceFirst":
positions.push(positions[0]);
if (normalFlow) {
positions[0] = nextPosition
} else {
positions[0] = prevPosition
}
break;
case "replaceLast":
if (normalFlow) {
positions.splice(1, 0, prevPosition)
} else {
positions.splice(1, 0, nextPosition)
}
break;
case "prepend":
positions.push(prevPosition);
break;
case "append":
positions.push(nextPosition)
}
return positions
}
});
var PanoramaThreeAndMoreItemsRenderStrategy = PanoramaItemsRenderStrategy.inherit({
updatePositions: function(offset) {
var $items = this._panorama._itemElements(),
movingBack = offset * this._getRTLSignCorrection() < 0,
positions = this._calculateItemPositions(this._panorama.option("selectedIndex"), movingBack);
$items.each(function(index) {
move($(this), positions[index] + offset)
})
},
animateRollback: function(selectedIndex) {
var $items = this._panorama._itemElements(),
positions = this._calculateItemPositions(selectedIndex),
animatingItems = [selectedIndex, this._normalizeIndex(selectedIndex + 1)];
if (this._isRTLEnabled() ^ position($items.eq(selectedIndex)) > this._startNextPosition()) {
animatingItems.push(this._normalizeIndex(selectedIndex - 1))
}
$items.each(function(index) {
var $item = $(this);
if (inArray(index, animatingItems) !== -1) {
animation.itemMove($item, positions[index])
} else {
move($item, positions[index])
}
})
},
detectBoundsTransition: function(newIndex, currentIndex) {
var lastIndex = this._indexBoundary() - 1;
if (currentIndex === lastIndex && 0 === newIndex) {
return "left"
}
if (0 === currentIndex && newIndex === lastIndex) {
return "right"
}
},
animateComplete: function(boundCross, newIndex, currentIndex) {
var animations = [],
$items = this._panorama._itemElements(),
positions = this._calculateItemPositions(newIndex);
var transitionBack = this._normalizeIndex(currentIndex - 1) === newIndex,
cyclingItemIndex = 3 === $items.length && transitionBack ? this._normalizeIndex(currentIndex + 1) : null,
cyclingItemPosition = positions[this._indexBoundary()];
var animatingItems = [newIndex, currentIndex],
backAnimatedItemIndex = transitionBack ? currentIndex : newIndex;
if (!transitionBack) {
animatingItems.push(this._normalizeIndex(backAnimatedItemIndex + 1))
}
$items.each(function(index) {
var $item = $(this);
if (inArray(index, animatingItems) === -1) {
move($item, positions[index]);
return
}
animations.push(index !== cyclingItemIndex ? animation.itemMove($item, positions[index]) : animation.itemMove($item, cyclingItemPosition, function() {
move($item, positions[index])
}))
});
return when.apply($, animations)
},
_calculateItemPositions: function(atIndex, movingBack) {
var previousIndex = this._normalizeIndex(atIndex - 1),
itemMargin = this._itemMargin(),
itemWidth = this._itemWidth(),
itemPositionOffset = (itemWidth + itemMargin) * this._getRTLSignCorrection(),
positions = [],
prevPosition = this._startPrevPosition(),
nextPosition = this._startNextPosition();
for (var i = atIndex; i !== previousIndex; i = this._normalizeIndex(i + 1)) {
positions[i] = nextPosition;
nextPosition += itemPositionOffset
}
if (movingBack) {
positions[previousIndex] = nextPosition;
nextPosition += itemPositionOffset
} else {
positions[previousIndex] = prevPosition
}
positions.push(nextPosition);
return positions
}
});
var Panorama = CollectionWidget.inherit({
_getDefaultOptions: function() {
return extend(this.callBase(), {
selectedIndex: 0,
title: "panorama",
backgroundImage: {
url: null,
width: 0,
height: 0
},
focusStateEnabled: false,
selectionMode: "single",
selectionRequired: true,
selectionByClick: false,
titleExpr: function(data) {
return data ? data.title : void 0
}
})
},
_itemClass: function() {
return PANORAMA_ITEM_CLASS
},
_itemDataKey: function() {
return PANORAMA_ITEM_DATA_KEY
},
_itemContainer: function() {
return this._$itemsContainer
},
_itemWidth: function() {
if (!this._itemWidthCache) {
this._itemWidthCache = this._itemElements().eq(0).outerWidth() || 0
}
return this._itemWidthCache
},
_clearItemWidthCache: function() {
delete this._itemWidthCache
},
_elementWidth: function() {
if (!this._elementWidthCache) {
this._elementWidthCache = this.$element().width()
}
return this._elementWidthCache
},
_clearElementWidthCache: function() {
delete this._elementWidthCache
},
_titleWidth: function() {
if (!this._titleWidthCache) {
this._titleWidthCache = this._$title.outerWidth()
}
return this._titleWidthCache
},
_clearTitleWidthCache: function() {
delete this._titleWidthCache
},
_init: function() {
this.callBase();
this._initItemsRenderStrategy();
this._initWrapper();
this._initTitle();
this._initItemsContainer();
this._initSwipeHandlers()
},
_dimensionChanged: function() {
this._clearItemWidthCache();
this._clearElementWidthCache();
this._clearTitleWidthCache();
this._updatePositions()
},
_initWrapper: function() {
this._$wrapper = $("<div>").addClass(PANORAMA_WRAPPER_CLASS).appendTo(this.$element())
},
_initItemsRenderStrategy: function() {
var itemsRenderStrategy;
switch (this.option("items").length) {
case 0:
case 1:
itemsRenderStrategy = PanoramaOneAndLessItemsRenderStrategy;
break;
case 2:
itemsRenderStrategy = PanoramaTwoItemsRenderStrategy;
break;
default:
itemsRenderStrategy = PanoramaThreeAndMoreItemsRenderStrategy
}
this._itemsRenderStrategy = new itemsRenderStrategy(this);
this._itemsRenderStrategy.init()
},
_initBackgroundImage: function() {
var backgroundUrl = this.option("backgroundImage.url");
if (backgroundUrl) {
this.$element().css("backgroundImage", "url(" + backgroundUrl + ")")
}
},
_initTitle: function() {
this._$title = $("<div>").addClass(PANORAMA_TITLE_CLASS);
this._$ghostTitle = $("<div>").addClass(PANORAMA_GHOST_TITLE_CLASS);
this._$wrapper.append(this._$title);
this._$wrapper.append(this._$ghostTitle);
this._updateTitle()
},
_updateTitle: function() {
var title = this.option("title");
this._$title.text(title);
this._$ghostTitle.text(title);
this._toggleGhostTitle(false)
},
_toggleGhostTitle: function(visible) {
var $ghostTitle = this._$ghostTitle;
if (visible) {
$ghostTitle.css("opacity", 1)
} else {
$ghostTitle.css("opacity", 0)
}
},
_getRTLSignCorrection: function() {
return this.option("rtlEnabled") ? -1 : 1
},
_initItemsContainer: function() {
this._$itemsContainer = $("<div>").addClass(PANORAMA_ITEMS_CONTAINER_CLASS);
this._$wrapper.append(this._$itemsContainer)
},
_initMarkup: function() {
this.$element().addClass(PANORAMA_CLASS);
this.callBase();
this._initBackgroundImage()
},
_render: function() {
this.callBase();
this._itemsRenderStrategy.render();
this._updateSelection()
},
_updatePositions: function(offset) {
offset = offset || 0;
this._updateBackgroundPosition(offset * this._calculateBackgroundStep());
this._updateTitlePosition(offset * this._calculateTitleStep());
this._itemsRenderStrategy.updatePositions(offset * this._elementWidth())
},
_updateBackgroundPosition: function(offset) {
moveBackground(this.$element(), this._calculateBackgroundPosition(this.option("selectedIndex")) + offset)
},
_updateTitlePosition: function(offset) {
move(this._$title, this._calculateTitlePosition(this.option("selectedIndex")) + offset)
},
_animateRollback: function(currentIndex) {
this._animateBackgroundMove(currentIndex);
this._animateTitleMove(currentIndex);
this._itemsRenderStrategy.animateRollback(currentIndex)
},
_animateBackgroundMove: function(toIndex) {
return animation.backgroundMove(this.$element(), this._calculateBackgroundPosition(toIndex))
},
_animateTitleMove: function(toIndex) {
return animation.titleMove(this._$title, this._calculateTitlePosition(toIndex))
},
_animateComplete: function(newIndex, currentIndex) {
var that = this,
boundCross = this._itemsRenderStrategy.detectBoundsTransition(newIndex, currentIndex);
var backgroundAnimation = this._performBackgroundAnimation(boundCross, newIndex);
var titleAnimation = this._performTitleAnimation(boundCross, newIndex);
var itemsAnimation = this._itemsRenderStrategy.animateComplete(boundCross, newIndex, currentIndex);
when(backgroundAnimation, titleAnimation, itemsAnimation).done(function() {
that._indexChangeOnAnimation = true;
that.option("selectedIndex", newIndex);
that._indexChangeOnAnimation = false
})
},
_performBackgroundAnimation: function(boundCross, newIndex) {
if (boundCross) {
return this._animateBackgroundBoundsTransition(boundCross, newIndex)
}
return this._animateBackgroundMove(newIndex)
},
_animateBackgroundBoundsTransition: function(bound, newIndex) {
var that = this,
isLeft = "left" === bound,
afterAnimationPosition = this._calculateBackgroundPosition(newIndex),
animationEndPositionShift = isLeft ^ this.option("rtlEnabled") ? -this._calculateBackgroundScaledWidth() : this._calculateBackgroundScaledWidth(),
animationEndPosition = afterAnimationPosition + animationEndPositionShift;
return animation.backgroundMove(this.$element(), animationEndPosition, function() {
moveBackground(that.$element(), afterAnimationPosition)
})
},
_performTitleAnimation: function(boundCross, newIndex) {
if (boundCross) {
return this._animateTitleBoundsTransition(boundCross, newIndex)
}
return this._animateTitleMove(newIndex)
},
_animateTitleBoundsTransition: function(bound, newIndex) {
var that = this,
$ghostTitle = this._$ghostTitle,
ghostWidth = this._titleWidth(),
panoramaWidth = this._elementWidth(),
isLeft = "left" === bound,
rtl = this.option("rtlEnabled"),
ghostTitleStartPosition = isLeft ^ rtl ? panoramaWidth : -ghostWidth,
ghostTitleEndPosition = isLeft ^ rtl ? -(panoramaWidth + ghostWidth) : panoramaWidth;
move($ghostTitle, ghostTitleStartPosition);
this._toggleGhostTitle(true);
this._swapGhostWithTitle();
var ghostAnimation = animation.titleMove($ghostTitle, ghostTitleEndPosition, function() {
that._toggleGhostTitle(false)
});
var titleAnimation = animation.titleMove(this._$title, this._calculateTitlePosition(newIndex));
return when(ghostAnimation, titleAnimation)
},
_swapGhostWithTitle: function() {
var $ghostTitle = this._$ghostTitle,
$title = this._$title,
lastTitlePosition = position($title);
move($title, position($ghostTitle));
move($ghostTitle, lastTitlePosition)
},
_calculateTitlePosition: function(atIndex) {
var panoramaWidth = this._elementWidth(),
titleWidth = this._titleWidth(),
titleMargin = panoramaWidth * PANORAMA_TITLE_MARGIN_SCALE,
titleStartPosition = this.option("rtlEnabled") ? panoramaWidth - titleMargin - titleWidth : titleMargin,
titleStep = atIndex * this._calculateTitleStep() * this._getRTLSignCorrection();
return titleStartPosition - titleStep
},
_calculateTitleStep: function() {
var panoramaWidth = this._elementWidth(),
titleWidth = this._titleWidth(),
indexBoundary = this._indexBoundary() || 1;
return Math.max((titleWidth - panoramaWidth) / indexBoundary, titleWidth / indexBoundary)
},
_calculateBackgroundPosition: function(atIndex) {
var panoramaWidth = this._elementWidth(),
backgroundScaledWidth = this._calculateBackgroundScaledWidth(),
backgroundStartPosition = this.option("rtlEnabled") ? panoramaWidth - backgroundScaledWidth : 0,
backgroundOffset = atIndex * this._calculateBackgroundStep() * this._getRTLSignCorrection();
return backgroundStartPosition - backgroundOffset
},
_calculateBackgroundStep: function() {
var itemWidth = this._itemWidth(),
backgroundScaledWidth = this._calculateBackgroundScaledWidth();
return Math.max((backgroundScaledWidth - itemWidth) / (this._indexBoundary() || 1), 0)
},
_calculateBackgroundScaledWidth: function() {
return this.$element().height() * this.option("backgroundImage.width") / (this.option("backgroundImage.height") || 1)
},
_initSwipeHandlers: function() {
eventsEngine.on(this.$element(), eventUtils.addNamespace(swipeEvents.start, this.NAME), {
itemSizeFunc: this._elementWidth.bind(this)
}, this._swipeStartHandler.bind(this));
eventsEngine.on(this.$element(), eventUtils.addNamespace(swipeEvents.swipe, this.NAME), this._swipeUpdateHandler.bind(this));
eventsEngine.on(this.$element(), eventUtils.addNamespace(swipeEvents.end, this.NAME), this._swipeEndHandler.bind(this))
},
_swipeStartHandler: function(e) {
this._stopAnimations();
e.maxLeftOffset = 1;
e.maxRightOffset = 1;
if (config().designMode || this.option("disabled") || this._indexBoundary() <= 1) {
e.cancel = true
}
},
_stopAnimations: function() {
endAnimation([this.$element(), this._$ghostTitle, this._$title]);
endAnimation(this._itemsRenderStrategy.allItemElements())
},
_swipeUpdateHandler: function(e) {
this._updatePositions(e.offset)
},
_swipeEndHandler: function(e) {
var currentIndex = this.option("selectedIndex"),
targetOffset = e.targetOffset * this._getRTLSignCorrection();
if (0 === targetOffset) {
this._animateRollback(currentIndex)
} else {
this._animateComplete(this._normalizeIndex(currentIndex - targetOffset), currentIndex)
}
},
_updateSelection: function() {
if (!this._indexChangeOnAnimation) {
this._updatePositions()
}
},
_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
},
_visibilityChanged: function(visible) {
if (visible) {
this._dimensionChanged()
}
},
_optionChanged: function(args) {
switch (args.name) {
case "width":
this.callBase(args);
this._dimensionChanged();
break;
case "backgroundImage":
this._invalidate();
break;
case "title":
this._updateTitle();
break;
case "items":
this._initItemsRenderStrategy();
this.callBase(args);
break;
case "titleExpr":
this._invalidate();
break;
default:
this.callBase(args)
}
}
});
Panorama.ItemClass = PanoramaItem;
registerComponent("dxPanorama", Panorama);
module.exports = Panorama;
module.exports.default = module.exports;