devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
252 lines (251 loc) • 9.91 kB
JavaScript
/**
* DevExtreme (esm/ui/collection/ui.collection_widget.live_update.js)
* Version: 21.1.4
* Build date: Mon Jun 21 2021
*
* Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import $ from "../../core/renderer";
import CollectionWidget from "./ui.collection_widget.edit";
import {
extend
} from "../../core/utils/extend";
import {
each
} from "../../core/utils/iterator";
import {
update,
insert,
indexByKey
} from "../../data/array_utils";
import dataUtils from "../../data/utils";
import {
when
} from "../../core/utils/deferred";
import {
findChanges
} from "../../core/utils/array_compare";
import domAdapter from "../../core/dom_adapter";
import {
noop
} from "../../core/utils/common";
var PRIVATE_KEY_FIELD = "__dx_key__";
export default CollectionWidget.inherit({
_getDefaultOptions: function() {
return extend(this.callBase(), {
repaintChangesOnly: false
})
},
ctor: function() {
this.callBase.apply(this, arguments);
this._customizeStoreLoadOptions = e => {
var dataSource = this._dataSource;
if (dataSource && !dataSource.isLoaded()) {
this._correctionIndex = 0
}
if (this._correctionIndex && e.storeLoadOptions) {
e.storeLoadOptions.skip += this._correctionIndex
}
}, this._dataSource && this._dataSource.on("customizeStoreLoadOptions", this._customizeStoreLoadOptions)
},
reload: function() {
this._correctionIndex = 0
},
_init: function() {
this.callBase();
this._refreshItemsCache();
this._correctionIndex = 0
},
_findItemElementByKey: function(key) {
var result = $();
var keyExpr = this.key();
this.itemElements().each((_, item) => {
var $item = $(item);
var itemData = this._getItemData($item);
if (keyExpr ? dataUtils.keysEqual(keyExpr, this.keyOf(itemData), key) : this._isItemEquals(itemData, key)) {
result = $item;
return false
}
});
return result
},
_dataSourceChangedHandler: function(newItems, e) {
if (null !== e && void 0 !== e && e.changes) {
this._modifyByChanges(e.changes)
} else {
this.callBase(newItems, e);
this._refreshItemsCache()
}
},
_isItemEquals: function(item1, item2) {
if (item1 && item1[PRIVATE_KEY_FIELD]) {
item1 = item1.data
}
try {
return JSON.stringify(item1) === JSON.stringify(item2)
} catch (e) {
return item1 === item2
}
},
_isItemStrictEquals: function(item1, item2) {
return this._isItemEquals(item1, item2)
},
_shouldAddNewGroup: function(changes, items) {
var result = false;
if (this.option("grouped")) {
each(changes, (i, change) => {
if ("insert" === change.type) {
result = true;
each(items, (_, item) => {
if (change.data.key === item.key) {
result = false;
return false
}
})
}
})
}
return result
},
_partialRefresh: function() {
if (this.option("repaintChangesOnly")) {
var result = findChanges(this._itemsCache, this._editStrategy.itemsGetter(), data => {
if (data && void 0 !== data[PRIVATE_KEY_FIELD]) {
return data[PRIVATE_KEY_FIELD]
}
return this.keyOf(data)
}, this._isItemStrictEquals.bind(this));
if (result && this._itemsCache.length && !this._shouldAddNewGroup(result, this._itemsCache)) {
this._modifyByChanges(result, true);
this._renderEmptyMessage();
return true
} else {
this._refreshItemsCache()
}
}
return false
},
_refreshItemsCache: function() {
if (this.option("repaintChangesOnly")) {
var items = this._editStrategy.itemsGetter();
try {
this._itemsCache = extend(true, [], items);
if (!this.key()) {
this._itemsCache = this._itemsCache.map((itemCache, index) => ({
[PRIVATE_KEY_FIELD]: items[index],
data: itemCache
}))
}
} catch (e) {
this._itemsCache = extend([], items)
}
}
},
_dispose: function() {
this._dataSource && this._dataSource.off("customizeStoreLoadOptions", this._customizeStoreLoadOptions);
this.callBase()
},
_updateByChange: function(keyInfo, items, change, isPartialRefresh) {
if (isPartialRefresh) {
this._renderItem(change.index, change.data, null, this._findItemElementByKey(change.key))
} else {
var changedItem = items[indexByKey(keyInfo, items, change.key)];
if (changedItem) {
update(keyInfo, items, change.key, change.data).done(() => {
this._renderItem(items.indexOf(changedItem), changedItem, null, this._findItemElementByKey(change.key))
})
}
}
},
_insertByChange: function(keyInfo, items, change, isPartialRefresh) {
when(isPartialRefresh || insert(keyInfo, items, change.data, change.index)).done(() => {
var _change$index;
this._beforeItemElementInserted(change);
var $itemContainer = this._getItemContainer(change.data);
this._renderItem(null !== (_change$index = change.index) && void 0 !== _change$index ? _change$index : items.length, change.data, $itemContainer);
this._afterItemElementInserted();
this._correctionIndex++
})
},
_getItemContainer: function(changeData) {
return this._itemContainer()
},
_updateSelectionAfterRemoveByChange: function(removeIndex) {
var selectedIndex = this.option("selectedIndex");
if (selectedIndex > removeIndex) {
this.option("selectedIndex", selectedIndex - 1)
} else if (selectedIndex === removeIndex && 1 === this.option("selectedItems").length) {
this.option("selectedItems", [])
} else {
this._normalizeSelectedItems()
}
},
_beforeItemElementInserted: function(change) {
var selectedIndex = this.option("selectedIndex");
if (change.index <= selectedIndex) {
this.option("selectedIndex", selectedIndex + 1)
}
},
_afterItemElementInserted: noop,
_removeByChange: function(keyInfo, items, change, isPartialRefresh) {
var index = isPartialRefresh ? change.index : indexByKey(keyInfo, items, change.key);
var removedItem = isPartialRefresh ? change.oldItem : items[index];
if (removedItem) {
var $removedItemElement = this._findItemElementByKey(change.key);
var deletedActionArgs = this._extendActionArgs($removedItemElement);
this._waitDeletingPrepare($removedItemElement).done(() => {
if (isPartialRefresh) {
this._updateIndicesAfterIndex(index - 1);
this._afterItemElementDeleted($removedItemElement, deletedActionArgs);
this._updateSelectionAfterRemoveByChange(index)
} else {
this._deleteItemElementByIndex(index);
this._afterItemElementDeleted($removedItemElement, deletedActionArgs)
}
});
this._correctionIndex--
}
},
_modifyByChanges: function(changes, isPartialRefresh) {
var items = this._editStrategy.itemsGetter();
var keyInfo = {
key: this.key.bind(this),
keyOf: this.keyOf.bind(this)
};
var dataSource = this._dataSource;
var paginate = dataSource && dataSource.paginate();
var group = dataSource && dataSource.group();
if (paginate || group) {
changes = changes.filter(item => "insert" !== item.type || void 0 !== item.index)
}
changes.forEach(change => this["_".concat(change.type, "ByChange")](keyInfo, items, change, isPartialRefresh));
this._renderedItemsCount = items.length;
this._refreshItemsCache();
this._fireContentReadyAction()
},
_appendItemToContainer: function($container, $itemFrame, index) {
var nextSiblingElement = $container.children(this._itemSelector()).get(index);
domAdapter.insertElement($container.get(0), $itemFrame.get(0), nextSiblingElement)
},
_optionChanged: function(args) {
switch (args.name) {
case "items":
var isItemsUpdated = this._partialRefresh(args.value);
if (!isItemsUpdated) {
this.callBase(args)
}
break;
case "dataSource":
if (!this.option("repaintChangesOnly") || !args.value) {
this.option("items", [])
}
this.callBase(args);
break;
case "repaintChangesOnly":
break;
default:
this.callBase(args)
}
}
});