devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
703 lines (700 loc) • 30.1 kB
JavaScript
/**
* DevExtreme (cjs/__internal/ui/collection/m_collection_widget.edit.js)
* Version: 24.2.6
* Build date: Mon Mar 17 2025
*
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _events_engine = _interopRequireDefault(require("../../../common/core/events/core/events_engine"));
var _data_source = require("../../../common/data/data_source/data_source");
var _utils = require("../../../common/data/data_source/utils");
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _common = require("../../../core/utils/common");
var _data = require("../../../core/utils/data");
var _deferred = require("../../../core/utils/deferred");
var _extend = require("../../../core/utils/extend");
var _iterator = require("../../../core/utils/iterator");
var _type = require("../../../core/utils/type");
var _ui = _interopRequireDefault(require("../../../ui/widget/ui.errors"));
var _collection_widget = _interopRequireDefault(require("../../ui/collection/collection_widget.base"));
var _m_collection_widgetEditStrategy = _interopRequireDefault(require("../../ui/collection/m_collection_widget.edit.strategy.plain"));
var _m_selection = _interopRequireDefault(require("../../ui/selection/m_selection"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : {
default: e
}
}
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function(n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) {
({}).hasOwnProperty.call(t, r) && (n[r] = t[r])
}
}
return n
}, _extends.apply(null, arguments)
}
const ITEM_DELETING_DATA_KEY = "dxItemDeleting";
const NOT_EXISTING_INDEX = -1;
const indexExists = index => -1 !== index;
class CollectionWidget extends _collection_widget.default {
_setOptionsByReference() {
super._setOptionsByReference();
(0, _extend.extend)(this._optionsByReference, {
selectedItem: true
})
}
_getDefaultOptions() {
return _extends({}, super._getDefaultOptions(), {
selectionMode: "none",
selectionRequired: false,
selectByClick: true,
selectedItems: [],
selectedItemKeys: [],
maxFilterLengthInRequest: 1500,
keyExpr: null,
selectedIndex: -1,
selectedItem: null,
onSelectionChanging: null,
onSelectionChanged: null,
onItemReordered: null,
onItemDeleting: null,
onItemDeleted: null
})
}
ctor(element, options) {
this._userOptions = options || {};
super.ctor(element, options)
}
_init() {
this._initEditStrategy();
super._init();
this._initKeyGetter();
this._initActions();
this._initSelectionModule()
}
_initKeyGetter() {
this._keyGetter = (0, _data.compileGetter)(this.option("keyExpr"))
}
_getActionsList() {
return ["onSelectionChanging", "onSelectionChanged"]
}
_initActions() {
this._actions = {};
const actions = this._getActionsList();
actions.forEach((action => {
this._actions[action] = this._createActionByOption(action, {
excludeValidators: ["disabled", "readOnly"]
}) ?? _common.noop
}))
}
_getKeysByItems(selectedItems) {
return this._editStrategy.getKeysByItems(selectedItems)
}
_getItemsByKeys(selectedItemKeys, selectedItems) {
return this._editStrategy.getItemsByKeys(selectedItemKeys, selectedItems)
}
_getKeyByIndex(index) {
return this._editStrategy.getKeyByIndex(index)
}
_getIndexByKey(key) {
return this._editStrategy.getIndexByKey(key)
}
_getIndexByItemData(itemData) {
return this._editStrategy.getIndexByItemData(itemData)
}
_isKeySpecified() {
return !!this._dataController.key()
}
_getCombinedFilter() {
return this._dataController.filter()
}
key() {
const {
keyExpr: keyExpr
} = this.option();
if (keyExpr) {
return keyExpr
}
return this._dataController.key()
}
keyOf(item) {
let key = item;
if (this.option("keyExpr")) {
key = this._keyGetter(item)
} else if (this._dataController.store()) {
key = this._dataController.keyOf(item)
}
return key
}
_nullValueSelectionSupported() {
return false
}
_initSelectionModule() {
const that = this;
const {
itemsGetter: itemsGetter
} = this._editStrategy;
this._selection = new _m_selection.default({
allowNullValue: this._nullValueSelectionSupported(),
mode: this.option("selectionMode"),
maxFilterLengthInRequest: this.option("maxFilterLengthInRequest"),
equalByReference: !this._isKeySpecified(),
onSelectionChanging: args => {
var _this$_actions$onSele, _this$_actions;
const isSelectionChanged = args.addedItemKeys.length || args.removedItemKeys.length;
if (!this._rendered || !isSelectionChanged) {
return
}
const selectionChangingArgs = {
removedItems: args.removedItems,
addedItems: args.addedItems,
cancel: false
};
null === (_this$_actions$onSele = (_this$_actions = this._actions).onSelectionChanging) || void 0 === _this$_actions$onSele || _this$_actions$onSele.call(_this$_actions, selectionChangingArgs);
args.cancel = selectionChangingArgs.cancel
},
onSelectionChanged: args => {
if (args.addedItemKeys.length || args.removedItemKeys.length) {
this.option("selectedItems", this._getItemsByKeys(args.selectedItemKeys, args.selectedItems));
this._updateSelectedItems(args)
}
},
filter: this._getCombinedFilter.bind(this),
totalCount: () => {
const {
items: items
} = this.option();
const totalCount = this._dataController.totalCount();
return totalCount >= 0 ? totalCount : this._getItemsCount(items)
},
key: this.key.bind(this),
keyOf: this.keyOf.bind(this),
load(options) {
var _dataController$loadO;
const dataController = that._dataController;
options.customQueryParams = null === (_dataController$loadO = dataController.loadOptions()) || void 0 === _dataController$loadO ? void 0 : _dataController$loadO.customQueryParams;
options.userData = dataController.userData();
if (dataController.store()) {
return dataController.loadFromStore(options).done((loadResult => {
if (that._disposed) {
return
}
const items = (0, _utils.normalizeLoadResult)(loadResult).data;
dataController.applyMapFunction(items)
}))
}
return (0, _deferred.Deferred)().resolve(this.plainItems())
},
dataFields: () => this._dataController.select(),
plainItems: itemsGetter.bind(this._editStrategy)
})
}
_getItemsCount(items) {
return items.reduce(((itemsCount, item) => itemsCount + (item.items ? this._getItemsCount(item.items) : 1)), 0)
}
_initEditStrategy() {
this._editStrategy = new _m_collection_widgetEditStrategy.default(this)
}
_getSelectedItemIndices(keys) {
const indices = [];
keys = keys || this._selection.getSelectedItemKeys();
this._editStrategy.beginCache();
(0, _iterator.each)(keys, ((_, key) => {
const selectedIndex = this._getIndexByKey(key);
if (indexExists(selectedIndex)) {
indices.push(selectedIndex)
}
}));
this._editStrategy.endCache();
return indices
}
_initMarkup() {
this._rendering = true;
if (!this._dataController.isLoading()) {
this._syncSelectionOptions().done((() => this._normalizeSelectedItems()))
}
super._initMarkup()
}
_render() {
super._render();
this._rendering = false
}
_fireContentReadyAction() {
this._rendering = false;
this._rendered = true;
super._fireContentReadyAction()
}
_syncSelectionOptions(byOption) {
byOption = byOption ?? this._chooseSelectOption();
let selectedItem;
let selectedIndex;
let selectedItemKeys;
let selectedItems;
switch (byOption) {
case "selectedIndex":
selectedItem = this._editStrategy.getItemDataByIndex(this.option("selectedIndex"));
if ((0, _type.isDefined)(selectedItem)) {
this._setOptionWithoutOptionChange("selectedItems", [selectedItem]);
this._setOptionWithoutOptionChange("selectedItem", selectedItem);
this._setOptionWithoutOptionChange("selectedItemKeys", this._editStrategy.getKeysByItems([selectedItem]))
} else {
this._setOptionWithoutOptionChange("selectedItems", []);
this._setOptionWithoutOptionChange("selectedItemKeys", []);
this._setOptionWithoutOptionChange("selectedItem", null)
}
break;
case "selectedItems":
selectedItems = this.option("selectedItems") || [];
selectedIndex = selectedItems.length ? this._editStrategy.getIndexByItemData(selectedItems[0]) : -1;
if (this.option("selectionRequired") && !indexExists(selectedIndex)) {
return this._syncSelectionOptions("selectedIndex")
}
this._setOptionWithoutOptionChange("selectedItem", selectedItems[0]);
this._setOptionWithoutOptionChange("selectedIndex", selectedIndex);
this._setOptionWithoutOptionChange("selectedItemKeys", this._editStrategy.getKeysByItems(selectedItems));
break;
case "selectedItem":
selectedItem = this.option("selectedItem");
selectedIndex = this._editStrategy.getIndexByItemData(selectedItem);
if (this.option("selectionRequired") && !indexExists(selectedIndex)) {
return this._syncSelectionOptions("selectedIndex")
}
if ((0, _type.isDefined)(selectedItem)) {
this._setOptionWithoutOptionChange("selectedItems", [selectedItem]);
this._setOptionWithoutOptionChange("selectedIndex", selectedIndex);
this._setOptionWithoutOptionChange("selectedItemKeys", this._editStrategy.getKeysByItems([selectedItem]))
} else {
this._setOptionWithoutOptionChange("selectedItems", []);
this._setOptionWithoutOptionChange("selectedItemKeys", []);
this._setOptionWithoutOptionChange("selectedIndex", -1)
}
break;
case "selectedItemKeys":
selectedItemKeys = this.option("selectedItemKeys");
if (this.option("selectionRequired")) {
const selectedItemIndex = this._getIndexByKey(selectedItemKeys[0]);
if (!indexExists(selectedItemIndex)) {
return this._syncSelectionOptions("selectedIndex")
}
}
return this._selection.setSelection(selectedItemKeys)
}
return (0, _deferred.Deferred)().resolve().promise()
}
_chooseSelectOption() {
let optionName = "selectedIndex";
const isOptionDefined = name => {
const optionValue = this.option(name);
const length = (0, _type.isDefined)(optionValue) && optionValue.length;
return length || name in this._userOptions
};
if (isOptionDefined("selectedItems")) {
optionName = "selectedItems"
} else if (isOptionDefined("selectedItem")) {
optionName = "selectedItem"
} else if (isOptionDefined("selectedItemKeys")) {
optionName = "selectedItemKeys"
}
return optionName
}
_compareKeys(oldKeys, newKeys) {
if (oldKeys.length !== newKeys.length) {
return false
}
for (let i = 0; i < newKeys.length; i++) {
if (oldKeys[i] !== newKeys[i]) {
return false
}
}
return true
}
_normalizeSelectedItems() {
const {
selectionMode: selectionMode,
selectedItems: selectedItems,
items: items
} = this.option();
if ("none" === selectionMode) {
this._setOptionWithoutOptionChange("selectedItems", []);
this._syncSelectionOptions("selectedItems")
} else if ("single" === selectionMode) {
const newSelection = selectedItems ?? [];
if (newSelection.length > 1 || !newSelection.length && this.option("selectionRequired") && items && items.length) {
const currentSelection = this._selection.getSelectedItems();
let normalizedSelection = void 0 === newSelection[0] ? currentSelection[0] : newSelection[0];
if (void 0 === normalizedSelection) {
normalizedSelection = this._editStrategy.itemsGetter()[0]
}
if (this.option("grouped") && normalizedSelection && normalizedSelection.items) {
normalizedSelection.items = [normalizedSelection.items[0]]
}
this._selection.setSelection(this._getKeysByItems([normalizedSelection]));
this._setOptionWithoutOptionChange("selectedItems", [normalizedSelection]);
return this._syncSelectionOptions("selectedItems")
}
this._selection.setSelection(this._getKeysByItems(newSelection))
} else {
const newKeys = this._getKeysByItems(this.option("selectedItems"));
const oldKeys = this._selection.getSelectedItemKeys();
if (!this._compareKeys(oldKeys, newKeys)) {
this._selection.setSelection(newKeys)
}
}
return (0, _deferred.Deferred)().resolve().promise()
}
_itemClickHandler(e, args, config) {
let itemSelectPromise = (0, _deferred.Deferred)().resolve();
this._createAction((e => {
itemSelectPromise = this._itemSelectHandler(e.event) ?? itemSelectPromise
}), {
validatingTargetName: "itemElement"
})({
itemElement: (0, _renderer.default)(e.currentTarget),
event: e
});
itemSelectPromise.always((() => {
super._itemClickHandler(e, args, config)
}))
}
_itemSelectHandler(e, shouldIgnoreSelectByClick) {
if (!shouldIgnoreSelectByClick && !this.option("selectByClick")) {
return
}
const $itemElement = e.currentTarget;
if (this.isItemSelected($itemElement)) {
this.unselectItem(e.currentTarget)
} else {
const itemSelectPromise = this.selectItem(e.currentTarget);
return null === itemSelectPromise || void 0 === itemSelectPromise ? void 0 : itemSelectPromise.promise()
}
}
_selectedItemElement(index) {
return this._itemElements().eq(index)
}
_postprocessRenderItem(args) {
const {
selectionMode: selectionMode
} = this.option();
if ("none" !== selectionMode) {
const $itemElement = (0, _renderer.default)(args.itemElement);
const normalizedItemIndex = this._editStrategy.getNormalizedIndex($itemElement);
const isItemSelected = this._isItemSelected(normalizedItemIndex);
this._processSelectableItem($itemElement, isItemSelected)
}
}
_processSelectableItem($itemElement, isSelected) {
$itemElement.toggleClass(this._selectedItemClass(), isSelected);
this._setAriaSelectionAttribute($itemElement, String(isSelected))
}
_updateSelectedItems(args) {
const {
addedItemKeys: addedItemKeys,
removedItemKeys: removedItemKeys
} = args;
if (this._rendered && (addedItemKeys.length || removedItemKeys.length)) {
if (!this._rendering) {
const addedSelection = [];
const removedSelection = [];
this._editStrategy.beginCache();
for (let i = 0; i < addedItemKeys.length; i += 1) {
const normalizedIndex = this._getIndexByKey(addedItemKeys[i]);
addedSelection.push(normalizedIndex);
this._addSelection(normalizedIndex)
}
for (let i = 0; i < removedItemKeys.length; i += 1) {
const normalizedIndex = this._getIndexByKey(removedItemKeys[i]);
removedSelection.push(normalizedIndex);
this._removeSelection(normalizedIndex)
}
this._editStrategy.endCache();
this._updateSelection(addedSelection, removedSelection)
}
this._actions.onSelectionChanged({
addedItems: args.addedItems,
removedItems: args.removedItems
})
}
}
_updateSelection(addedSelection, removedSelection) {}
_setAriaSelectionAttribute($target, value) {
this.setAria("selected", value, $target)
}
_removeSelection(normalizedIndex) {
const $itemElement = this._editStrategy.getItemElement(normalizedIndex);
if (indexExists(normalizedIndex)) {
this._processSelectableItem($itemElement, false);
_events_engine.default.triggerHandler($itemElement, "stateChanged", false)
}
}
_addSelection(normalizedIndex) {
const $itemElement = this._editStrategy.getItemElement(normalizedIndex);
if (indexExists(normalizedIndex)) {
this._processSelectableItem($itemElement, true);
_events_engine.default.triggerHandler($itemElement, "stateChanged", true)
}
}
_isItemSelected(index) {
const key = this._getKeyByIndex(index);
return this._selection.isItemSelected(key, {
checkPending: true
})
}
_optionChanged(args) {
switch (args.name) {
case "selectionMode":
this._invalidate();
break;
case "dataSource":
if (!args.value || Array.isArray(args.value) && !args.value.length) {
this.option("selectedItemKeys", [])
}
super._optionChanged(args);
break;
case "selectedIndex":
case "selectedItem":
case "selectedItems":
case "selectedItemKeys":
this._syncSelectionOptions(args.name).done((() => this._normalizeSelectedItems()));
break;
case "keyExpr":
this._initKeyGetter();
break;
case "selectionRequired":
this._normalizeSelectedItems();
break;
case "onSelectionChanging":
case "onSelectionChanged":
this._initActions();
break;
case "selectByClick":
case "onItemDeleting":
case "onItemDeleted":
case "onItemReordered":
case "maxFilterLengthInRequest":
break;
default:
super._optionChanged(args)
}
}
_clearSelectedItems() {
this._setOptionWithoutOptionChange("selectedItems", []);
this._syncSelectionOptions("selectedItems")
}
_waitDeletingPrepare($itemElement) {
if ($itemElement.data("dxItemDeleting")) {
return (0, _deferred.Deferred)().resolve().promise()
}
$itemElement.data("dxItemDeleting", true);
const deferred = (0, _deferred.Deferred)();
const deletingActionArgs = {
cancel: false
};
const deletePromise = this._itemEventHandler($itemElement, "onItemDeleting", deletingActionArgs, {
excludeValidators: ["disabled", "readOnly"]
});
(0, _deferred.when)(deletePromise).always((function(value) {
const deletePromiseExists = !deletePromise;
const deletePromiseResolved = !deletePromiseExists && "resolved" === deletePromise.state();
const argumentsSpecified = !!arguments.length;
const shouldDelete = deletePromiseExists || deletePromiseResolved && !argumentsSpecified || deletePromiseResolved && value;
(0, _deferred.when)((0, _deferred.fromPromise)(deletingActionArgs.cancel)).always((() => {
$itemElement.data("dxItemDeleting", false)
})).done((cancel => {
if (shouldDelete && !cancel) {
deferred.resolve()
} else {
deferred.reject()
}
})).fail(deferred.reject)
}));
return deferred.promise()
}
_deleteItemFromDS($item) {
const dataController = this._dataController;
const deferred = (0, _deferred.Deferred)();
const disabledState = this.option("disabled");
const dataStore = dataController.store();
if (!dataStore) {
return (0, _deferred.Deferred)().resolve().promise()
}
if (!dataStore.remove) {
throw _ui.default.Error("E1011")
}
this.option("disabled", true);
dataStore.remove(dataController.keyOf(this._getItemData($item))).done((key => {
if (void 0 !== key) {
deferred.resolve()
} else {
deferred.reject()
}
})).fail((() => {
deferred.reject()
}));
deferred.always((() => {
this.option("disabled", disabledState)
}));
return deferred
}
_tryRefreshLastPage() {
const deferred = (0, _deferred.Deferred)();
if (this._isLastPage() || this.option("grouped")) {
deferred.resolve()
} else {
this._refreshLastPage().done((() => {
deferred.resolve()
}))
}
return deferred.promise()
}
_refreshLastPage() {
this._expectLastItemLoading();
return this._dataController.load()
}
_updateSelectionAfterDelete(index) {
const key = this._getKeyByIndex(index);
this._selection.deselect([key])
}
_updateIndicesAfterIndex(index) {
const itemElements = this._itemElements();
for (let i = index + 1; i < itemElements.length; i += 1) {
(0, _renderer.default)(itemElements[i]).data(this._itemIndexKey(), i - 1)
}
}
_simulateOptionChange(optionName) {
var _this$_optionChangedA;
const optionValue = this.option(optionName);
if (optionValue instanceof _data_source.DataSource) {
return
}
null === (_this$_optionChangedA = this._optionChangedAction) || void 0 === _this$_optionChangedA || _this$_optionChangedA.call(this, {
name: optionName,
fullName: optionName,
value: optionValue
})
}
isItemSelected(itemElement) {
return this._isItemSelected(this._editStrategy.getNormalizedIndex(itemElement))
}
selectItem(itemElement) {
const {
selectionMode: selectionMode
} = this.option();
if ("none" === selectionMode) {
return (0, _deferred.Deferred)().resolve()
}
const itemIndex = this._editStrategy.getNormalizedIndex(itemElement);
if (!indexExists(itemIndex)) {
return (0, _deferred.Deferred)().resolve()
}
const key = this._getKeyByIndex(itemIndex);
if (this._selection.isItemSelected(key)) {
return (0, _deferred.Deferred)().resolve()
}
if ("single" === selectionMode) {
return this._selection.setSelection([key])
}
const {
selectedItemKeys: selectedItemKeys
} = this.option();
return this._selection.setSelection([...selectedItemKeys ?? [], key], [key])
}
unselectItem(itemElement) {
const itemIndex = this._editStrategy.getNormalizedIndex(itemElement);
if (!indexExists(itemIndex)) {
return
}
const selectedItemKeys = this._selection.getSelectedItemKeys();
if (this.option("selectionRequired") && selectedItemKeys.length <= 1) {
return
}
const key = this._getKeyByIndex(itemIndex);
if (!this._selection.isItemSelected(key, {
checkPending: true
})) {
return
}
this._selection.deselect([key])
}
_deleteItemElementByIndex(index) {
this._updateSelectionAfterDelete(index);
this._updateIndicesAfterIndex(index);
this._editStrategy.deleteItemAtIndex(index)
}
_afterItemElementDeleted($item, deletedActionArgs) {
const changingOption = this._dataController.getDataSource() ? "dataSource" : "items";
this._simulateOptionChange(changingOption);
this._itemEventHandler($item, "onItemDeleted", deletedActionArgs, {
beforeExecute() {
$item.remove()
},
excludeValidators: ["disabled", "readOnly"]
});
this._renderEmptyMessage()
}
deleteItem(itemElement) {
const deferred = (0, _deferred.Deferred)();
const $item = this._editStrategy.getItemElement(itemElement);
const index = this._editStrategy.getNormalizedIndex(itemElement);
const itemResponseWaitClass = this._itemResponseWaitClass();
if (indexExists(index)) {
this._waitDeletingPrepare($item).done((() => {
$item.addClass(itemResponseWaitClass);
const deletedActionArgs = this._extendActionArgs($item);
this._deleteItemFromDS($item).done((() => {
this._deleteItemElementByIndex(index);
this._afterItemElementDeleted($item, deletedActionArgs);
this._tryRefreshLastPage().done((() => {
deferred.resolveWith(this)
}))
})).fail((() => {
$item.removeClass(itemResponseWaitClass);
deferred.rejectWith(this)
}))
})).fail((() => {
deferred.rejectWith(this)
}))
} else {
deferred.rejectWith(this)
}
return deferred.promise()
}
reorderItem(itemElement, toItemElement) {
const deferred = (0, _deferred.Deferred)();
const strategy = this._editStrategy;
const $movingItem = strategy.getItemElement(itemElement);
const $destinationItem = strategy.getItemElement(toItemElement);
const movingIndex = strategy.getNormalizedIndex(itemElement);
const destinationIndex = strategy.getNormalizedIndex(toItemElement);
const changingOption = this._dataController.getDataSource() ? "dataSource" : "items";
const canMoveItems = indexExists(movingIndex) && indexExists(destinationIndex) && movingIndex !== destinationIndex;
if (canMoveItems) {
deferred.resolveWith(this)
} else {
deferred.rejectWith(this)
}
return deferred.promise().done((() => {
$destinationItem[strategy.itemPlacementFunc(movingIndex, destinationIndex)]($movingItem);
strategy.moveItemAtIndexToIndex(movingIndex, destinationIndex);
this._updateIndicesAfterIndex(movingIndex);
this.option("selectedItems", this._getItemsByKeys(this._selection.getSelectedItemKeys(), this._selection.getSelectedItems()));
if ("items" === changingOption) {
this._simulateOptionChange(changingOption)
}
this._itemEventHandler($movingItem, "onItemReordered", {
fromIndex: strategy.getIndex(movingIndex),
toIndex: strategy.getIndex(destinationIndex)
}, {
excludeValidators: ["disabled", "readOnly"]
})
}))
}
}
var _default = exports.default = CollectionWidget;