devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
424 lines (422 loc) • 17.6 kB
JavaScript
/**
* DevExtreme (cjs/ui/multi_view.js)
* Version: 23.2.6
* Build date: Wed May 01 2024
*
* Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
exports.default = void 0;
var _size = require("../core/utils/size");
var _renderer = _interopRequireDefault(require("../core/renderer"));
var _translator2 = require("../animation/translator");
var _uiMulti_view = require("./multi_view/ui.multi_view.animation");
var _math = require("../core/utils/math");
var _extend = require("../core/utils/extend");
var _common = require("../core/utils/common");
var _visibility_change = require("../events/visibility_change");
var _element = require("../core/element");
var _type = require("../core/utils/type");
var _devices = _interopRequireDefault(require("../core/devices"));
var _component_registrator = _interopRequireDefault(require("../core/component_registrator"));
var _uiCollection_widget = _interopRequireDefault(require("./collection/ui.collection_widget.live_update"));
var _swipeable = _interopRequireDefault(require("../events/gesture/swipeable"));
var _deferred = require("../core/utils/deferred");
var _message = _interopRequireDefault(require("../localization/message"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
}
}
const MULTIVIEW_CLASS = "dx-multiview";
const MULTIVIEW_WRAPPER_CLASS = "dx-multiview-wrapper";
const MULTIVIEW_ITEM_CONTAINER_CLASS = "dx-multiview-item-container";
const MULTIVIEW_ITEM_CLASS = "dx-multiview-item";
const MULTIVIEW_ITEM_HIDDEN_CLASS = "dx-multiview-item-hidden";
const MULTIVIEW_ITEM_DATA_KEY = "dxMultiViewItemData";
const MULTIVIEW_ANIMATION_DURATION = 200;
const toNumber = value => +value;
const position = $element => (0, _translator2.locate)($element).left;
const MultiView = _uiCollection_widget.default.inherit({
_activeStateUnit: ".dx-multiview-item",
_supportedKeys: function() {
return (0, _extend.extend)(this.callBase(), {
pageUp: _common.noop,
pageDown: _common.noop
})
},
_getDefaultOptions: function() {
return (0, _extend.extend)(this.callBase(), {
selectedIndex: 0,
swipeEnabled: true,
animationEnabled: true,
loop: false,
deferRendering: true,
loopItemFocus: false,
selectOnFocus: true,
selectionMode: "single",
selectionRequired: true,
selectByClick: false
})
},
_defaultOptionsRules: function() {
return this.callBase().concat([{
device: function() {
return "desktop" === _devices.default.real().deviceType && !_devices.default.isSimulator()
},
options: {
focusStateEnabled: true
}
}])
},
_itemClass: function() {
return "dx-multiview-item"
},
_itemDataKey: function() {
return "dxMultiViewItemData"
},
_itemContainer: function() {
return this._$itemContainer
},
_itemElements: function() {
return this._itemContainer().children(this._itemSelector())
},
_itemWidth: function() {
if (!this._itemWidthValue) {
this._itemWidthValue = (0, _size.getWidth)(this._$wrapper)
}
return this._itemWidthValue
},
_clearItemWidthCache: function() {
delete this._itemWidthValue
},
_itemsCount: function() {
return this.option("items").length
},
_normalizeIndex: function(index) {
const count = this._itemsCount();
if (index < 0) {
index += count
}
if (index >= count) {
index -= count
}
return index
},
_getRTLSignCorrection: function() {
return this.option("rtlEnabled") ? -1 : 1
},
_init: function() {
this.callBase.apply(this, arguments);
const $element = this.$element();
$element.addClass("dx-multiview");
this._$wrapper = (0, _renderer.default)("<div>").addClass("dx-multiview-wrapper");
this._$wrapper.appendTo($element);
this._$itemContainer = (0, _renderer.default)("<div>").addClass("dx-multiview-item-container");
this._$itemContainer.appendTo(this._$wrapper);
this.option("loopItemFocus", this.option("loop"));
this._findBoundaryIndices();
this._initSwipeable()
},
_initMarkup: function() {
this._deferredItems = [];
this.callBase();
const selectedItemIndices = this._getSelectedItemIndices();
this._updateItemsVisibility(selectedItemIndices[0]);
this._setElementAria();
this._setItemsAria()
},
_afterItemElementDeleted: function($item, deletedActionArgs) {
this.callBase($item, deletedActionArgs);
if (this._deferredItems) {
this._deferredItems.splice(deletedActionArgs.itemIndex, 1)
}
},
_beforeItemElementInserted: function(change) {
this.callBase.apply(this, arguments);
if (this._deferredItems) {
this._deferredItems.splice(change.index, 0, null)
}
},
_executeItemRenderAction: function(index, itemData, itemElement) {
index = (this.option("items") || []).indexOf(itemData);
this.callBase(index, itemData, itemElement)
},
_renderItemContent: function(args) {
const renderContentDeferred = new _deferred.Deferred;
const that = this;
const callBase = this.callBase;
const deferred = new _deferred.Deferred;
deferred.done((function() {
const $itemContent = callBase.call(that, args);
renderContentDeferred.resolve($itemContent)
}));
this._deferredItems[args.index] = deferred;
this.option("deferRendering") || deferred.resolve();
return renderContentDeferred.promise()
},
_render: function() {
this.callBase();
(0, _common.deferRender)(() => {
const selectedItemIndices = this._getSelectedItemIndices();
this._updateItems(selectedItemIndices[0])
})
},
_getElementAria: () => ({
role: "group",
roledescription: _message.default.format("dxMultiView-elementAriaRoleDescription"),
label: _message.default.format("dxMultiView-elementAriaLabel")
}),
_setElementAria() {
const aria = this._getElementAria();
this.setAria(aria, this.$element())
},
_setItemsAria() {
const $itemElements = this._itemElements();
const itemsCount = this._itemsCount();
$itemElements.each((itemIndex, item) => {
const aria = this._getItemAria({
itemIndex: itemIndex,
itemsCount: itemsCount
});
this.setAria(aria, (0, _renderer.default)(item))
})
},
_getItemAria(_ref) {
let {
itemIndex: itemIndex,
itemsCount: itemsCount
} = _ref;
const aria = {
role: "group",
roledescription: _message.default.format("dxMultiView-itemAriaRoleDescription"),
label: _message.default.format("dxMultiView-itemAriaLabel", itemIndex + 1, itemsCount)
};
return aria
},
_updateItems: function(selectedIndex, newIndex) {
this._updateItemsPosition(selectedIndex, newIndex);
this._updateItemsVisibility(selectedIndex, newIndex)
},
_modifyByChanges: function() {
this.callBase.apply(this, arguments);
const selectedItemIndices = this._getSelectedItemIndices();
this._updateItemsVisibility(selectedItemIndices[0])
},
_updateItemsPosition: function(selectedIndex, newIndex) {
const $itemElements = this._itemElements();
const positionSign = (0, _type.isDefined)(newIndex) ? -this._animationDirection(newIndex, selectedIndex) : void 0;
const $selectedItem = $itemElements.eq(selectedIndex);
_uiMulti_view._translator.move($selectedItem, 0);
if ((0, _type.isDefined)(newIndex)) {
_uiMulti_view._translator.move($itemElements.eq(newIndex), 100 * positionSign + "%")
}
},
_updateItemsVisibility(selectedIndex, newIndex) {
const $itemElements = this._itemElements();
$itemElements.each((itemIndex, item) => {
const $item = (0, _renderer.default)(item);
const isHidden = itemIndex !== selectedIndex && itemIndex !== newIndex;
if (!isHidden) {
this._renderSpecificItem(itemIndex)
}
$item.toggleClass("dx-multiview-item-hidden", isHidden);
this.setAria("hidden", isHidden || void 0, $item)
})
},
_renderSpecificItem: function(index) {
const $item = this._itemElements().eq(index);
const hasItemContent = $item.find(this._itemContentClass()).length > 0;
if ((0, _type.isDefined)(index) && !hasItemContent) {
this._deferredItems[index].resolve();
(0, _visibility_change.triggerResizeEvent)($item)
}
},
_refreshItem: function($item, item) {
this.callBase($item, item);
this._updateItemsVisibility(this.option("selectedIndex"))
},
_setAriaSelectionAttribute: _common.noop,
_updateSelection: function(addedSelection, removedSelection) {
const newIndex = addedSelection[0];
const prevIndex = removedSelection[0];
_uiMulti_view.animation.complete(this._$itemContainer);
this._updateItems(prevIndex, newIndex);
const animationDirection = this._animationDirection(newIndex, prevIndex);
this._animateItemContainer(animationDirection * this._itemWidth(), function() {
_uiMulti_view._translator.move(this._$itemContainer, 0);
this._updateItems(newIndex);
(0, _size.getWidth)(this._$itemContainer)
}.bind(this))
},
_animateItemContainer: function(position, completeCallback) {
const duration = this.option("animationEnabled") ? 200 : 0;
_uiMulti_view.animation.moveTo(this._$itemContainer, position, duration, completeCallback)
},
_animationDirection: function(newIndex, prevIndex) {
const containerPosition = ($element = this._$itemContainer, (0, _translator2.locate)($element).left);
var $element;
const indexDifference = (prevIndex - newIndex) * this._getRTLSignCorrection() * this._getItemFocusLoopSignCorrection();
const isSwipePresent = 0 !== containerPosition;
const directionSignVariable = isSwipePresent ? containerPosition : indexDifference;
return (0, _math.sign)(directionSignVariable)
},
_getSwipeDisabledState() {
return !this.option("swipeEnabled") || this._itemsCount() <= 1
},
_initSwipeable() {
this._createComponent(this.$element(), _swipeable.default, {
disabled: this._getSwipeDisabledState(),
elastic: false,
itemSizeFunc: this._itemWidth.bind(this),
onStart: args => this._swipeStartHandler(args.event),
onUpdated: args => this._swipeUpdateHandler(args.event),
onEnd: args => this._swipeEndHandler(args.event)
})
},
_findBoundaryIndices() {
var _firstIndex2, _lastIndex;
const items = this.option("items");
let firstIndex;
let lastIndex;
items.forEach((item, index) => {
const isDisabled = Boolean(null === item || void 0 === item ? void 0 : item.disabled);
if (!isDisabled) {
var _firstIndex;
null !== (_firstIndex = firstIndex) && void 0 !== _firstIndex ? _firstIndex : firstIndex = index;
lastIndex = index
}
});
this._boundaryIndices = {
firstAvailableIndex: null !== (_firstIndex2 = firstIndex) && void 0 !== _firstIndex2 ? _firstIndex2 : 0,
lastAvailableIndex: null !== (_lastIndex = lastIndex) && void 0 !== _lastIndex ? _lastIndex : items.length - 1,
firstTrueIndex: 0,
lastTrueIndex: items.length - 1
}
},
_swipeStartHandler: function(e) {
_uiMulti_view.animation.complete(this._$itemContainer);
const selectedIndex = this.option("selectedIndex");
const loop = this.option("loop");
const {
firstAvailableIndex: firstAvailableIndex,
lastAvailableIndex: lastAvailableIndex
} = this._boundaryIndices;
const rtl = this.option("rtlEnabled");
e.maxLeftOffset = toNumber(loop || (rtl ? selectedIndex > firstAvailableIndex : selectedIndex < lastAvailableIndex));
e.maxRightOffset = toNumber(loop || (rtl ? selectedIndex < lastAvailableIndex : selectedIndex > firstAvailableIndex));
this._swipeDirection = null
},
_swipeUpdateHandler: function(e) {
const offset = e.offset;
const swipeDirection = (0, _math.sign)(offset) * this._getRTLSignCorrection();
_uiMulti_view._translator.move(this._$itemContainer, offset * this._itemWidth());
if (swipeDirection !== this._swipeDirection) {
this._swipeDirection = swipeDirection;
const selectedIndex = this.option("selectedIndex");
const newIndex = this._normalizeIndex(selectedIndex - swipeDirection);
this._updateItems(selectedIndex, newIndex)
}
},
_findNextAvailableIndex(index, offset) {
const {
items: items,
loop: loop
} = this.option();
const {
firstAvailableIndex: firstAvailableIndex,
lastAvailableIndex: lastAvailableIndex,
firstTrueIndex: firstTrueIndex,
lastTrueIndex: lastTrueIndex
} = this._boundaryIndices;
const isFirstActive = [firstTrueIndex, firstAvailableIndex].includes(index);
const isLastActive = [lastTrueIndex, lastAvailableIndex].includes(index);
if (loop) {
if (isFirstActive && offset < 0) {
return lastAvailableIndex
} else if (isLastActive && offset > 0) {
return firstAvailableIndex
}
}
for (let i = index + offset; i >= firstAvailableIndex && i <= lastAvailableIndex; i += offset) {
const isDisabled = Boolean(items[i].disabled);
if (!isDisabled) {
return i
}
}
return index
},
_swipeEndHandler: function(e) {
const targetOffset = e.targetOffset * this._getRTLSignCorrection();
if (targetOffset) {
const newSelectedIndex = this._findNextAvailableIndex(this.option("selectedIndex"), -targetOffset);
this.option("selectedIndex", newSelectedIndex);
const $selectedElement = this.itemElements().filter(".dx-item-selected");
this.option("focusStateEnabled") && this.option("focusedElement", (0, _element.getPublicElement)($selectedElement))
} else {
this._animateItemContainer(0, _common.noop)
}
},
_getItemFocusLoopSignCorrection: function() {
return this._itemFocusLooped ? -1 : 1
},
_moveFocus: function() {
this.callBase.apply(this, arguments);
this._itemFocusLooped = false
},
_prevItem: function($items) {
const $result = this.callBase.apply(this, arguments);
this._itemFocusLooped = $result.is($items.last());
return $result
},
_nextItem: function($items) {
const $result = this.callBase.apply(this, arguments);
this._itemFocusLooped = $result.is($items.first());
return $result
},
_dimensionChanged: function() {
this._clearItemWidthCache()
},
_visibilityChanged: function(visible) {
if (visible) {
this._dimensionChanged()
}
},
_updateSwipeDisabledState() {
const disabled = this._getSwipeDisabledState();
_swipeable.default.getInstance(this.$element()).option("disabled", disabled)
},
_dispose: function() {
delete this._boundaryIndices;
this.callBase()
},
_optionChanged: function(args) {
const value = args.value;
switch (args.name) {
case "loop":
this.option("loopItemFocus", value);
break;
case "animationEnabled":
break;
case "swipeEnabled":
this._updateSwipeDisabledState();
break;
case "deferRendering":
this._invalidate();
break;
case "items":
this._updateSwipeDisabledState();
this._findBoundaryIndices();
this.callBase(args);
break;
default:
this.callBase(args)
}
}
});
(0, _component_registrator.default)("dxMultiView", MultiView);
var _default = MultiView;
exports.default = _default;
module.exports = exports.default;
module.exports.default = exports.default;