devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,273 lines (1,084 loc) • 41 kB
JavaScript
"use strict";
var $ = require("../../core/renderer"),
eventsEngine = require("../../events/core/events_engine"),
commonUtils = require("../../core/utils/common"),
typeUtils = require("../../core/utils/type"),
getPublicElement = require("../../core/utils/dom").getPublicElement,
each = require("../../core/utils/iterator").each,
compileGetter = require("../../core/utils/data").compileGetter,
extend = require("../../core/utils/extend").extend,
fx = require("../../animation/fx"),
clickEvent = require("../../events/click"),
swipeEvents = require("../../events/swipe"),
support = require("../../core/utils/support"),
messageLocalization = require("../../localization/message"),
inkRipple = require("../widget/utils.ink_ripple"),
devices = require("../../core/devices"),
ListItem = require("./item"),
Button = require("../button"),
eventUtils = require("../../events/utils"),
themes = require("../themes"),
windowUtils = require("../../core/utils/window"),
ScrollView = require("../scroll_view"),
deviceDependentOptions = require("../scroll_view/ui.scrollable").deviceDependentOptions,
CollectionWidget = require("../collection/ui.collection_widget.edit"),
BindableTemplate = require("../widget/bindable_template"),
Deferred = require("../../core/utils/deferred").Deferred;
var LIST_CLASS = "dx-list",
LIST_ITEM_CLASS = "dx-list-item",
LIST_ITEM_SELECTOR = "." + LIST_ITEM_CLASS,
LIST_GROUP_CLASS = "dx-list-group",
LIST_GROUP_HEADER_CLASS = "dx-list-group-header",
LIST_GROUP_BODY_CLASS = "dx-list-group-body",
LIST_COLLAPSIBLE_GROUPS_CLASS = "dx-list-collapsible-groups",
LIST_GROUP_COLLAPSED_CLASS = "dx-list-group-collapsed",
LIST_GROUP_HEADER_INDICATOR_CLASS = "dx-list-group-header-indicator",
LIST_HAS_NEXT_CLASS = "dx-has-next",
LIST_NEXT_BUTTON_CLASS = "dx-list-next-button",
SELECT_ALL_SELECTOR = ".dx-list-select-all",
LIST_ITEM_DATA_KEY = "dxListItemData",
LIST_FEEDBACK_SHOW_TIMEOUT = 70;
var groupItemsGetter = compileGetter("items");
var ListBase = CollectionWidget.inherit({
_activeStateUnit: [LIST_ITEM_SELECTOR, SELECT_ALL_SELECTOR].join(","),
_supportedKeys: function _supportedKeys() {
var that = this;
var moveFocusPerPage = function moveFocusPerPage(direction) {
var $item = getEdgeVisibleItem(direction),
isFocusedItem = $item.is(that.option("focusedElement"));
if (isFocusedItem) {
scrollListTo($item, direction);
$item = getEdgeVisibleItem(direction);
}
that.option("focusedElement", getPublicElement($item));
that.scrollToItem($item);
};
var getEdgeVisibleItem = function getEdgeVisibleItem(direction) {
var scrollTop = that.scrollTop(),
containerHeight = that.$element().height();
var $item = $(that.option("focusedElement")),
isItemVisible = true;
if (!$item.length) {
return $();
}
while (isItemVisible) {
var $nextItem = $item[direction]();
if (!$nextItem.length) {
break;
}
var nextItemLocation = $nextItem.position().top + $nextItem.outerHeight() / 2;
isItemVisible = nextItemLocation < containerHeight + scrollTop && nextItemLocation > scrollTop;
if (isItemVisible) {
$item = $nextItem;
}
}
return $item;
};
var scrollListTo = function scrollListTo($item, direction) {
var resultPosition = $item.position().top;
if (direction === "prev") {
resultPosition = $item.position().top - that.$element().height() + $item.outerHeight();
}
that.scrollTo(resultPosition);
};
return extend(this.callBase(), {
leftArrow: commonUtils.noop,
rightArrow: commonUtils.noop,
pageUp: function pageUp() {
moveFocusPerPage("prev");
return false;
},
pageDown: function pageDown() {
moveFocusPerPage("next");
return false;
}
});
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name dxListOptions.hoverStateEnabled
* @publicName hoverStateEnabled
* @type boolean
* @default true
* @inheritdoc
*/
hoverStateEnabled: true,
/**
* @name dxListOptions.pullRefreshEnabled
* @publicName pullRefreshEnabled
* @type boolean
* @default false
*/
pullRefreshEnabled: false,
/**
* @name dxListOptions.scrollingEnabled
* @publicName scrollingEnabled
* @type boolean
* @default true
*/
scrollingEnabled: true,
/**
* @name dxListOptions.showScrollbar
* @publicName showScrollbar
* @type Enums.ShowScrollbarMode
* @default 'onScroll'
* @default 'onHover' @for desktop
*/
showScrollbar: "onScroll",
useNativeScrolling: true,
/**
* @name dxListOptions.bounceEnabled
* @publicName bounceEnabled
* @type boolean
* @default true
* @default false @for desktop
*/
bounceEnabled: true,
/**
* @name dxListOptions.scrollByContent
* @publicName scrollByContent
* @type boolean
* @default true
* @default false @for non-touch_devices
*/
scrollByContent: true,
/**
* @name dxListOptions.scrollByThumb
* @publicName scrollByThumb
* @type boolean
* @default false
* @default true @for desktop
*/
scrollByThumb: false,
/**
* @name dxListOptions.pullingDownText
* @publicName pullingDownText
* @type string
* @default "Pull down to refresh..."
*/
pullingDownText: messageLocalization.format("dxList-pullingDownText"),
/**
* @name dxListOptions.pulledDownText
* @publicName pulledDownText
* @type string
* @default "Release to refresh..."
*/
pulledDownText: messageLocalization.format("dxList-pulledDownText"),
/**
* @name dxListOptions.refreshingText
* @publicName refreshingText
* @type string
* @default "Refreshing..."
*/
refreshingText: messageLocalization.format("dxList-refreshingText"),
/**
* @name dxListOptions.pageLoadingText
* @publicName pageLoadingText
* @type string
* @default "Loading..."
*/
pageLoadingText: messageLocalization.format("dxList-pageLoadingText"),
/**
* @name dxListOptions.onScroll
* @publicName onScroll
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field5 event:event
* @type_function_param1_field6 scrollOffset:object
* @type_function_param1_field7 reachedLeft:boolean
* @type_function_param1_field8 reachedRight:boolean
* @type_function_param1_field9 reachedTop:boolean
* @type_function_param1_field10 reachedBottom:boolean
* @action
*/
onScroll: null,
/**
* @name dxListOptions.onPullRefresh
* @publicName onPullRefresh
* @extends Action
* @action
*/
onPullRefresh: null,
/**
* @name dxListOptions.onPageLoading
* @publicName onPageLoading
* @extends Action
* @action
*/
onPageLoading: null,
/**
* @name dxListOptions.pageLoadMode
* @publicName pageLoadMode
* @type Enums.ListPageLoadMode
* @default "scrollBottom"
*/
pageLoadMode: "scrollBottom",
/**
* @name dxListOptions.nextButtonText
* @publicName nextButtonText
* @type string
* @default "More"
*/
nextButtonText: messageLocalization.format("dxList-nextButtonText"),
/**
* @name dxListOptions.onItemSwipe
* @publicName onItemSwipe
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 jQueryEvent:jQuery.Event:deprecated(event)
* @type_function_param1_field5 event:event
* @type_function_param1_field6 itemData:object
* @type_function_param1_field7 itemElement:dxElement
* @type_function_param1_field8 itemIndex:number | object
* @type_function_param1_field9 direction:string
* @action
*/
onItemSwipe: null,
/**
* @name dxListOptions.grouped
* @publicName grouped
* @type boolean
* @default false
*/
grouped: false,
/**
* @name dxListOptions.onGroupRendered
* @publicName onGroupRendered
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field4 groupData:object
* @type_function_param1_field5 groupElement:dxElement
* @type_function_param1_field6 groupIndex:number
* @action
*/
onGroupRendered: null,
/**
* @name dxListOptions.collapsibleGroups
* @publicName collapsibleGroups
* @type boolean
* @default false
*/
collapsibleGroups: false,
/**
* @name dxListOptions.groupTemplate
* @publicName groupTemplate
* @type template|function
* @default "group"
* @type_function_param1 groupData:object
* @type_function_param2 groupIndex:number
* @type_function_param3 groupElement:dxElement
* @type_function_return string|Node|jQuery
*/
groupTemplate: "group",
/**
* @name dxListOptions.indicateLoading
* @publicName indicateLoading
* @type boolean
* @default true
*/
indicateLoading: true,
/**
* @name dxListOptions.selectedIndex
* @publicName selectedIndex
* @type number
* @default -1
* @hidden
*/
/**
* @name dxListOptions.selectedItem
* @publicName selectedItem
* @hidden
* @inheritdoc
*/
/**
* @name dxListOptions.activeStateEnabled
* @publicName activeStateEnabled
* @type boolean
* @default true
* @inheritdoc
*/
activeStateEnabled: true,
_itemAttributes: { "role": "option" },
useInkRipple: false,
/**
* @name dxListOptions.onItemClick
* @publicName onItemClick
* @type function(e)|string
* @extends Action
* @type_function_param1 e:object
* @type_function_param1_field4 itemData:object
* @type_function_param1_field5 itemElement:dxElement
* @type_function_param1_field6 itemIndex:number | object
* @action
*/
/**
* @name dxListOptions.onItemContextMenu
* @publicName onItemContextMenu
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field6 itemIndex:number | object
* @action
* @inheritdoc
*/
/**
* @name dxListOptions.onItemHold
* @publicName onItemHold
* @extends Action
* @type function(e)
* @type_function_param1 e:object
* @type_function_param1_field6 itemIndex:number | object
* @action
* @inheritdoc
*/
showChevronExpr: function showChevronExpr(data) {
return data ? data.showChevron : undefined;
},
badgeExpr: function badgeExpr(data) {
return data ? data.badge : undefined;
}
/**
* @name dxListItemTemplate
* @publicName dxListItemTemplate
* @inherits CollectionWidgetItemTemplate
* @type object
*/
/**
* @name dxListItemTemplate.badge
* @publicName badge
* @type String
*/
/**
* @name dxListItemTemplate.showChevron
* @publicName showChevron
* @type boolean
*/
/**
* @name dxListItemTemplate.key
* @publicName key
* @type String
*/
});
},
_defaultOptionsRules: function _defaultOptionsRules() {
/**
* @name dxListOptions.useNativeScrolling
* @publicName useNativeScrolling
* @default false @for desktop
* @default true @for Mac
*/
return this.callBase().concat(deviceDependentOptions(), [{
device: function device() {
return !support.nativeScrolling;
},
options: {
/**
* @name dxListOptions.useNativeScrolling
* @publicName useNativeScrolling
* @type boolean
* @default true
*/
useNativeScrolling: false
}
}, {
device: function device(_device) {
return !support.nativeScrolling && !devices.isSimulator() && devices.real().platform === "generic" && _device.platform === "generic";
},
options: {
/**
* @name dxListOptions.showScrollbar
* @publicName showScrollbar
* @default 'onHover' @for desktop
*/
showScrollbar: "onHover",
/**
* @name dxListOptions.pageLoadMode
* @publicName pageLoadMode
* @default 'nextButton' @for desktop
*/
pageLoadMode: "nextButton"
}
}, {
device: function device() {
return devices.real().deviceType === "desktop" && !devices.isSimulator();
},
options: {
/**
* @name dxListOptions.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 devices.current().platform === "win" && devices.isSimulator();
},
options: {
bounceEnabled: false
}
}, {
device: function device() {
return themes.isMaterial();
},
options: {
/**
* @name dxListOptions.pullingDownText
* @publicName pullingDownText
* @type string
* @default "" @for Material
*/
pullingDownText: "",
/**
* @name dxListOptions.pulledDownText
* @publicName pulledDownText
* @type string
* @default "" @for Material
*/
pulledDownText: "",
/**
* @name dxListOptions.refreshingText
* @publicName refreshingText
* @type string
* @default "" @for Material
*/
refreshingText: "",
/**
* @name dxListOptions.pageLoadingText
* @publicName pageLoadingText
* @type string
* @default "" @for Material
*/
pageLoadingText: "",
useInkRipple: true
}
}]);
},
_visibilityChanged: function _visibilityChanged(visible) {
if (visible) {
this._updateLoadingState(true);
}
},
_itemClass: function _itemClass() {
return LIST_ITEM_CLASS;
},
_itemDataKey: function _itemDataKey() {
return LIST_ITEM_DATA_KEY;
},
_itemContainer: function _itemContainer() {
return this._$container;
},
_refreshItemElements: function _refreshItemElements() {
if (!this.option("grouped")) {
this._itemElementsCache = this._itemContainer().children(this._itemSelector());
} else {
this._itemElementsCache = this._itemContainer().children("." + LIST_GROUP_CLASS).children("." + LIST_GROUP_BODY_CLASS).children(this._itemSelector());
}
},
reorderItem: function reorderItem(itemElement, toItemElement) {
var promise = this.callBase(itemElement, toItemElement);
return promise.done(function () {
this._refreshItemElements();
});
},
deleteItem: function deleteItem(itemElement) {
var promise = this.callBase(itemElement);
return promise.done(function () {
this._refreshItemElements();
});
},
_itemElements: function _itemElements() {
return this._itemElementsCache;
},
_itemSelectHandler: function _itemSelectHandler(e) {
if (this.option("selectionMode") === "single" && this.isItemSelected(e.currentTarget)) {
return;
}
this.callBase(e);
},
_allowDynamicItemsAppend: function _allowDynamicItemsAppend() {
return true;
},
_init: function _init() {
this.callBase();
this._$container = this.$element();
this._initScrollView();
this._feedbackShowTimeout = LIST_FEEDBACK_SHOW_TIMEOUT;
this._createGroupRenderAction();
this.setAria("role", "listbox");
},
_scrollBottomMode: function _scrollBottomMode() {
return this.option("pageLoadMode") === "scrollBottom";
},
_nextButtonMode: function _nextButtonMode() {
return this.option("pageLoadMode") === "nextButton";
},
_dataSourceOptions: function _dataSourceOptions() {
var scrollBottom = this._scrollBottomMode(),
nextButton = this._nextButtonMode();
return extend(this.callBase(), {
paginate: commonUtils.ensureDefined(scrollBottom || nextButton, true)
});
},
_dataSourceFromUrlLoadMode: function _dataSourceFromUrlLoadMode() {
return "raw";
},
_initScrollView: function _initScrollView() {
var scrollingEnabled = this.option("scrollingEnabled"),
pullRefreshEnabled = scrollingEnabled && this.option("pullRefreshEnabled"),
autoPagingEnabled = scrollingEnabled && this._scrollBottomMode() && !!this._dataSource;
this._scrollView = this._createComponent(this.$element(), ScrollView, {
disabled: this.option("disabled") || !scrollingEnabled,
onScroll: this._scrollHandler.bind(this),
onPullDown: pullRefreshEnabled ? this._pullDownHandler.bind(this) : null,
onReachBottom: autoPagingEnabled ? this._scrollBottomHandler.bind(this) : null,
showScrollbar: this.option("showScrollbar"),
useNative: this.option("useNativeScrolling"),
bounceEnabled: this.option("bounceEnabled"),
scrollByContent: this.option("scrollByContent"),
scrollByThumb: this.option("scrollByThumb"),
pullingDownText: this.option("pullingDownText"),
pulledDownText: this.option("pulledDownText"),
refreshingText: this.option("refreshingText"),
reachBottomText: this.option("pageLoadingText"),
useKeyboard: false
});
this._$container = $(this._scrollView.content());
this._createScrollViewActions();
},
_createScrollViewActions: function _createScrollViewActions() {
this._scrollAction = this._createActionByOption("onScroll");
this._pullRefreshAction = this._createActionByOption("onPullRefresh");
this._pageLoadingAction = this._createActionByOption("onPageLoading");
},
_scrollHandler: function _scrollHandler(e) {
this._scrollAction && this._scrollAction(e);
},
_initTemplates: function _initTemplates() {
this.callBase();
this._defaultTemplates["group"] = new BindableTemplate(function ($container, data) {
if (typeUtils.isPlainObject(data)) {
if (data.key) {
$container.text(data.key);
}
} else {
$container.text(String(data));
}
}, ["key"], this.option("integrationOptions.watchMethod"));
},
_updateLoadingState: function _updateLoadingState(tryLoadMore) {
var isDataLoaded = !tryLoadMore || this._isLastPage(),
scrollBottomMode = this._scrollBottomMode(),
stopLoading = isDataLoaded || !scrollBottomMode,
hideLoadIndicator = stopLoading && !this._isDataSourceLoading();
if (stopLoading || this._scrollViewIsFull()) {
this._scrollView.release(hideLoadIndicator);
this._toggleNextButton(this._shouldRenderNextButton() && !this._isLastPage());
this._loadIndicationSuppressed(false);
} else {
this._infiniteDataLoading();
}
},
_shouldRenderNextButton: function _shouldRenderNextButton() {
return this._nextButtonMode() && this._dataSource && this._dataSource.isLoaded();
},
_dataSourceLoadingChangedHandler: function _dataSourceLoadingChangedHandler(isLoading) {
if (this._loadIndicationSuppressed()) {
return;
}
if (isLoading && this.option("indicateLoading")) {
this._showLoadingIndicatorTimer = setTimeout(function () {
var isEmpty = !this._itemElements().length;
if (this._scrollView && !isEmpty) {
this._scrollView.startLoading();
}
}.bind(this));
} else {
clearTimeout(this._showLoadingIndicatorTimer);
this._scrollView && this._scrollView.finishLoading();
}
},
_dataSourceChangedHandler: function _dataSourceChangedHandler(newItems) {
if (!this._shouldAppendItems() && windowUtils.hasWindow()) {
this._scrollView && this._scrollView.scrollTo(0);
}
this.callBase(newItems);
},
_refreshContent: function _refreshContent() {
this._prepareContent();
this._fireContentReadyAction();
},
_hideLoadingIfLoadIndicationOff: function _hideLoadingIfLoadIndicationOff() {
if (!this.option("indicateLoading")) {
this._dataSourceLoadingChangedHandler(false);
}
},
_loadIndicationSuppressed: function _loadIndicationSuppressed(value) {
if (!arguments.length) {
return this._isLoadIndicationSuppressed;
}
this._isLoadIndicationSuppressed = value;
},
_scrollViewIsFull: function _scrollViewIsFull() {
return !this._scrollView || this._scrollView.isFull();
},
_pullDownHandler: function _pullDownHandler(e) {
this._pullRefreshAction(e);
if (this._dataSource && !this._isDataSourceLoading()) {
this._clearSelectedItems();
this._dataSource.pageIndex(0);
this._dataSource.reload();
} else {
this._updateLoadingState();
}
},
_infiniteDataLoading: function _infiniteDataLoading() {
var isElementVisible = this.$element().is(":visible");
if (isElementVisible && !this._scrollViewIsFull() && !this._isDataSourceLoading() && !this._isLastPage()) {
clearTimeout(this._loadNextPageTimer);
this._loadNextPageTimer = setTimeout(this._loadNextPage.bind(this));
}
},
_scrollBottomHandler: function _scrollBottomHandler(e) {
this._pageLoadingAction(e);
if (!this._isDataSourceLoading() && !this._isLastPage()) {
this._loadNextPage();
} else {
this._updateLoadingState();
}
},
_renderItems: function _renderItems(items) {
if (this.option("grouped")) {
each(items, this._renderGroup.bind(this));
this._attachGroupCollapseEvent();
this._renderEmptyMessage();
if (themes.isMaterial()) {
this.attachGroupHeaderInkRippleEvents();
}
} else {
this.callBase.apply(this, arguments);
}
this._refreshItemElements();
this._updateLoadingState(true);
},
_attachGroupCollapseEvent: function _attachGroupCollapseEvent() {
var eventName = eventUtils.addNamespace(clickEvent.name, this.NAME),
selector = "." + LIST_GROUP_HEADER_CLASS,
$element = this.$element(),
collapsibleGroups = this.option("collapsibleGroups");
$element.toggleClass(LIST_COLLAPSIBLE_GROUPS_CLASS, collapsibleGroups);
eventsEngine.off($element, eventName, selector);
if (collapsibleGroups) {
eventsEngine.on($element, eventName, selector, function (e) {
this._createAction(function (e) {
var $group = $(e.event.currentTarget).parent();
this._collapseGroupHandler($group);
if (this.option("focusStateEnabled")) {
this.option("focusedElement", getPublicElement($group.find("." + LIST_ITEM_CLASS).eq(0)));
}
}.bind(this), {
validatingTargetName: "element"
})({
event: e
});
}.bind(this));
}
},
_collapseGroupHandler: function _collapseGroupHandler($group, toggle) {
var deferred = new Deferred();
if ($group.hasClass(LIST_GROUP_COLLAPSED_CLASS) === toggle) {
return deferred.resolve();
}
var $groupBody = $group.children("." + LIST_GROUP_BODY_CLASS);
var startHeight = $groupBody.outerHeight();
var endHeight = startHeight === 0 ? $groupBody.height("auto").outerHeight() : 0;
$group.toggleClass(LIST_GROUP_COLLAPSED_CLASS, toggle);
fx.animate($groupBody, {
type: "custom",
from: { height: startHeight },
to: { height: endHeight },
duration: 200,
complete: function () {
this.updateDimensions();
this._updateLoadingState();
deferred.resolve();
}.bind(this)
});
return deferred.promise();
},
_dataSourceLoadErrorHandler: function _dataSourceLoadErrorHandler() {
this._forgetNextPageLoading();
if (this._initialized) {
this._renderEmptyMessage();
this._updateLoadingState();
}
},
_initMarkup: function _initMarkup() {
this._itemElementsCache = $();
this.$element().addClass(LIST_CLASS);
this.callBase();
this.option("useInkRipple") && this._renderInkRipple();
},
_renderInkRipple: function _renderInkRipple() {
this._inkRipple = inkRipple.render();
},
_toggleActiveState: function _toggleActiveState($element, value, e) {
this.callBase.apply(this, arguments);
var that = this;
if (!this._inkRipple) {
return;
}
var config = {
element: $element,
event: e
};
if (value) {
if (themes.isMaterial()) {
this._inkRippleTimer = setTimeout(function () {
that._inkRipple.showWave(config);
}, LIST_FEEDBACK_SHOW_TIMEOUT / 2);
} else {
that._inkRipple.showWave(config);
}
} else {
this._inkRipple.hideWave(config);
}
},
_postprocessRenderItem: function _postprocessRenderItem(args) {
this._refreshItemElements();
this.callBase.apply(this, arguments);
if (this.option("onItemSwipe")) {
this._attachSwipeEvent($(args.itemElement));
}
},
_attachSwipeEvent: function _attachSwipeEvent($itemElement) {
var endEventName = eventUtils.addNamespace(swipeEvents.end, this.NAME);
eventsEngine.on($itemElement, endEventName, this._itemSwipeEndHandler.bind(this));
},
_itemSwipeEndHandler: function _itemSwipeEndHandler(e) {
this._itemDXEventHandler(e, "onItemSwipe", {
direction: e.offset < 0 ? "left" : "right"
});
},
_nextButtonHandler: function _nextButtonHandler() {
var source = this._dataSource;
if (source && !source.isLoading()) {
this._scrollView.toggleLoading(true);
this._$nextButton.detach();
this._loadIndicationSuppressed(true);
this._loadNextPage();
}
},
_renderGroup: function _renderGroup(index, group) {
var $groupElement = $("<div>").addClass(LIST_GROUP_CLASS).appendTo(this._itemContainer());
var $groupHeaderElement = $("<div>").addClass(LIST_GROUP_HEADER_CLASS).appendTo($groupElement);
var groupTemplateName = this.option("groupTemplate"),
groupTemplate = this._getTemplate(group.template || groupTemplateName, group, index, $groupHeaderElement),
renderArgs = {
index: index,
itemData: group,
container: getPublicElement($groupHeaderElement)
};
this._createItemByTemplate(groupTemplate, renderArgs);
if (themes.isMaterial()) {
$("<div>").addClass(LIST_GROUP_HEADER_INDICATOR_CLASS).prependTo($groupHeaderElement);
}
this._renderingGroupIndex = index;
var $groupBody = $("<div>").addClass(LIST_GROUP_BODY_CLASS).appendTo($groupElement);
each(groupItemsGetter(group) || [], function (index, item) {
this._renderItem(index, item, $groupBody);
}.bind(this));
this._groupRenderAction({
groupElement: getPublicElement($groupElement),
groupIndex: index,
groupData: group
});
},
attachGroupHeaderInkRippleEvents: function attachGroupHeaderInkRippleEvents() {
var that = this,
selector = "." + LIST_GROUP_HEADER_CLASS,
$element = this.$element();
eventsEngine.on($element, "dxpointerdown", selector, function (e) {
that._toggleActiveState($(e.currentTarget), true, e);
});
eventsEngine.on($element, "dxpointerup dxhoverend", selector, function (e) {
that._toggleActiveState($(e.currentTarget), false);
});
},
_createGroupRenderAction: function _createGroupRenderAction() {
this._groupRenderAction = this._createActionByOption("onGroupRendered");
},
_clean: function _clean() {
clearTimeout(this._inkRippleTimer);
if (this._$nextButton) {
this._$nextButton.remove();
this._$nextButton = null;
}
this.callBase.apply(this, arguments);
},
_dispose: function _dispose() {
clearTimeout(this._holdTimer);
clearTimeout(this._loadNextPageTimer);
clearTimeout(this._showLoadingIndicatorTimer);
this.callBase();
},
_toggleDisabledState: function _toggleDisabledState(value) {
this.callBase(value);
this._scrollView.option("disabled", value || !this.option("scrollingEnabled"));
},
_toggleNextButton: function _toggleNextButton(value) {
var dataSource = this._dataSource,
$nextButton = this._getNextButton();
this.$element().toggleClass(LIST_HAS_NEXT_CLASS, value);
if (value && dataSource && dataSource.isLoaded()) {
$nextButton.appendTo(this._itemContainer());
}
if (!value) {
$nextButton.detach();
}
},
_getNextButton: function _getNextButton() {
if (!this._$nextButton) {
this._$nextButton = this._createNextButton();
}
return this._$nextButton;
},
_createNextButton: function _createNextButton() {
var $result = $("<div>").addClass(LIST_NEXT_BUTTON_CLASS);
var $button = $("<div>").appendTo($result);
this._createComponent($button, Button, {
text: this.option("nextButtonText"),
onClick: this._nextButtonHandler.bind(this),
type: themes.isMaterial() ? "default" : undefined,
integrationOptions: {}
});
return $result;
},
_moveFocus: function _moveFocus() {
this.callBase.apply(this, arguments);
this.scrollToItem(this.option("focusedElement"));
},
_refresh: function _refresh() {
if (!windowUtils.hasWindow()) {
this.callBase();
} else {
var scrollTop = this._scrollView.scrollTop();
this.callBase();
scrollTop && this._scrollView.scrollTo(scrollTop);
}
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "pageLoadMode":
this._toggleNextButton(args.value);
this._initScrollView();
break;
case "dataSource":
this.callBase(args);
this._initScrollView();
break;
case "pullingDownText":
case "pulledDownText":
case "refreshingText":
case "pageLoadingText":
case "useNative":
case "showScrollbar":
case "bounceEnabled":
case "scrollByContent":
case "scrollByThumb":
case "scrollingEnabled":
case "pullRefreshEnabled":
this._initScrollView();
this._updateLoadingState();
break;
case "nextButtonText":
case "onItemSwipe":
case "useInkRipple":
this._invalidate();
break;
case "onScroll":
case "onPullRefresh":
case "onPageLoading":
this._createScrollViewActions();
this._invalidate();
break;
case "grouped":
case "collapsibleGroups":
case "groupTemplate":
this._invalidate();
break;
case "onGroupRendered":
this._createGroupRenderAction();
break;
case "width":
case "height":
this.callBase(args);
this._scrollView.update();
break;
case "indicateLoading":
this._hideLoadingIfLoadIndicationOff();
break;
case "visible":
this.callBase(args);
this._scrollView.update();
break;
case "rtlEnabled":
this._initScrollView();
this.callBase(args);
break;
case "showChevronExpr":
case "badgeExpr":
this._invalidate();
break;
default:
this.callBase(args);
}
},
_extendActionArgs: function _extendActionArgs($itemElement) {
if (!this.option("grouped")) {
return this.callBase($itemElement);
}
var $group = $itemElement.closest("." + LIST_GROUP_CLASS);
var $item = $group.find("." + LIST_ITEM_CLASS);
return extend(this.callBase($itemElement), {
itemIndex: {
group: $group.index(),
item: $item.index($itemElement)
}
});
},
/**
* @name dxListMethods.expandGroup
* @publicName expandGroup(groupIndex)
* @param1 groupIndex:Number
* @return Promise<void>
*/
expandGroup: function expandGroup(groupIndex) {
var deferred = new Deferred(),
$group = this._itemContainer().find("." + LIST_GROUP_CLASS).eq(groupIndex);
this._collapseGroupHandler($group, false).done(function () {
deferred.resolveWith(this);
}.bind(this));
return deferred.promise();
},
/**
* @name dxListMethods.collapseGroup
* @publicName collapseGroup(groupIndex)
* @param1 groupIndex:Number
* @return Promise<void>
*/
collapseGroup: function collapseGroup(groupIndex) {
var deferred = new Deferred(),
$group = this._itemContainer().find("." + LIST_GROUP_CLASS).eq(groupIndex);
this._collapseGroupHandler($group, true).done(function () {
deferred.resolveWith(this);
}.bind(this));
return deferred;
},
/**
* @name dxListMethods.updateDimensions
* @publicName updateDimensions()
* @return Promise<void>
*/
updateDimensions: function updateDimensions() {
var that = this,
deferred = new Deferred();
if (that._scrollView) {
that._scrollView.update().done(function () {
!that._scrollViewIsFull() && that._updateLoadingState(true);
deferred.resolveWith(that);
});
} else {
deferred.resolveWith(that);
}
return deferred.promise();
},
/**
* @name dxListMethods.reload
* @publicName reload()
*/
reload: function reload() {
this.scrollTo(0);
this._pullDownHandler();
},
repaint: function repaint() {
this.scrollTo(0);
this.callBase();
},
/**
* @name dxListMethods.scrollTop
* @publicName scrollTop()
* @return numeric
*/
scrollTop: function scrollTop() {
return this._scrollView.scrollOffset().top;
},
/**
* @name dxListMethods.clientHeight
* @publicName clientHeight()
* @return numeric
*/
clientHeight: function clientHeight() {
return this._scrollView.clientHeight();
},
/**
* @name dxListMethods.scrollHeight
* @publicName scrollHeight()
* @return numeric
*/
scrollHeight: function scrollHeight() {
return this._scrollView.scrollHeight();
},
/**
* @name dxListMethods.scrollBy
* @publicName scrollBy(distance)
* @param1 distance:numeric
*/
scrollBy: function scrollBy(distance) {
this._scrollView.scrollBy(distance);
},
/**
* @name dxListMethods.scrollTo
* @publicName scrollTo(location)
* @param1 location:numeric
*/
scrollTo: function scrollTo(location) {
this._scrollView.scrollTo(location);
},
/**
* @name dxListMethods.scrollToItem
* @publicName scrollToItem(itemElement)
* @param1 itemElement:Node
*/
/**
* @name dxListMethods.scrollToItem
* @publicName scrollToItem(itemIndex)
* @param1 itemIndex:Number|Object
*/
scrollToItem: function scrollToItem(itemElement) {
var $item = this._editStrategy.getItemElement(itemElement);
this._scrollView.scrollToElement($item);
}
});
ListBase.ItemClass = ListItem;
module.exports = ListBase;
///#DEBUG
module.exports.mockScrollView = function (Mock) {
ScrollView = Mock;
};
///#ENDDEBUG