devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
669 lines (664 loc) • 29.4 kB
JavaScript
/**
* DevExtreme (cjs/__internal/grids/grid_core/m_utils.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 = _interopRequireDefault(require("../../../common/data/data_source"));
var _utils = require("../../../common/data/data_source/utils");
var _utils2 = require("../../../common/data/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 _position = require("../../../core/utils/position");
var _size = require("../../../core/utils/size");
var _string = require("../../../core/utils/string");
var _type = require("../../../core/utils/type");
var _variable_wrapper = _interopRequireDefault(require("../../../core/utils/variable_wrapper"));
var _window = require("../../../core/utils/window");
var _format_helper = _interopRequireDefault(require("../../../format_helper"));
var _load_panel = _interopRequireDefault(require("../../../ui/load_panel"));
var _filtering = _interopRequireDefault(require("../../../ui/shared/filtering"));
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 DATAGRID_SELECTION_DISABLED_CLASS = "dx-selection-disabled";
const DATAGRID_GROUP_OPENED_CLASS = "dx-datagrid-group-opened";
const DATAGRID_GROUP_CLOSED_CLASS = "dx-datagrid-group-closed";
const DATAGRID_EXPAND_CLASS = "dx-datagrid-expand";
const NO_DATA_CLASS = "nodata";
const SCROLLING_MODE_INFINITE = "infinite";
const SCROLLING_MODE_VIRTUAL = "virtual";
const LEGACY_SCROLLING_MODE = "scrolling.legacyMode";
const SCROLLING_MODE_OPTION = "scrolling.mode";
const ROW_RENDERING_MODE_OPTION = "scrolling.rowRenderingMode";
const DATE_INTERVAL_SELECTORS = {
year: value => value && value.getFullYear(),
month: value => value && value.getMonth() + 1,
day: value => value && value.getDate(),
quarter: value => value && Math.floor(value.getMonth() / 3) + 1,
hour: value => value && value.getHours(),
minute: value => value && value.getMinutes(),
second: value => value && value.getSeconds()
};
const getIntervalSelector = function() {
const data = arguments[1];
const value = this.calculateCellValue(data);
if (!(0, _type.isDefined)(value)) {
return null
}
if (isDateType(this.dataType)) {
const nameIntervalSelector = arguments[0];
return DATE_INTERVAL_SELECTORS[nameIntervalSelector](value)
}
if ("number" === this.dataType) {
const groupInterval = arguments[0];
return Math.floor(Number(value) / groupInterval) * groupInterval
}
};
const equalSelectors = function(selector1, selector2) {
if ((0, _type.isFunction)(selector1) && (0, _type.isFunction)(selector2)) {
if (selector1.originalCallback && selector2.originalCallback) {
return selector1.originalCallback === selector2.originalCallback && selector1.columnIndex === selector2.columnIndex
}
}
return selector1 === selector2
};
function isDateType(dataType) {
return "date" === dataType || "datetime" === dataType
}
const setEmptyText = function($container) {
$container.get(0).textContent = "\xa0"
};
const normalizeSortingInfo = function(sort) {
sort = sort || [];
const result = (0, _utils2.normalizeSortingInfo)(sort);
for (let i = 0; i < sort.length; i++) {
if (sort && sort[i] && void 0 !== sort[i].isExpanded) {
result[i].isExpanded = sort[i].isExpanded
}
if (sort && sort[i] && void 0 !== sort[i].groupInterval) {
result[i].groupInterval = sort[i].groupInterval
}
}
return result
};
const formatValue = function(value, options) {
const valueText = _format_helper.default.format(value, options.format) || value && value.toString() || "";
const formatObject = {
value: value,
valueText: options.getDisplayFormat ? options.getDisplayFormat(valueText) : valueText,
target: options.target || "row",
groupInterval: options.groupInterval
};
return options.customizeText ? options.customizeText.call(options, formatObject) : formatObject.valueText
};
const getSummaryText = function(summaryItem, summaryTexts) {
const displayFormat = summaryItem.displayFormat || summaryItem.columnCaption && summaryTexts[`${summaryItem.summaryType}OtherColumn`] || summaryTexts[summaryItem.summaryType];
return formatValue(summaryItem.value, {
format: summaryItem.valueFormat,
getDisplayFormat: valueText => displayFormat ? (0, _string.format)(displayFormat, valueText, summaryItem.columnCaption) : valueText,
customizeText: summaryItem.customizeText
})
};
const getWidgetInstance = function($element) {
const editorData = $element.data && $element.data();
const dxComponents = editorData && editorData.dxComponents;
const widgetName = dxComponents && dxComponents[0];
return widgetName && editorData[widgetName]
};
const equalFilterParameters = function(filter1, filter2) {
if (Array.isArray(filter1) && Array.isArray(filter2)) {
if (filter1.length !== filter2.length) {
return false
}
for (let i = 0; i < filter1.length; i++) {
if (!equalFilterParameters(filter1[i], filter2[i])) {
return false
}
}
return true
}
if ((0, _type.isFunction)(filter1) && filter1.columnIndex >= 0 && (0, _type.isFunction)(filter2) && filter2.columnIndex >= 0) {
return filter1.columnIndex === filter2.columnIndex && (0, _data.toComparable)(filter1.filterValue) === (0, _data.toComparable)(filter2.filterValue) && (0, _data.toComparable)(filter1.selectedFilterOperation) === (0, _data.toComparable)(filter2.selectedFilterOperation)
}
return (0, _data.toComparable)(filter1) == (0, _data.toComparable)(filter2)
};
const createPoint = options => ({
index: options.index,
columnIndex: options.columnIndex,
x: options.x,
y: options.y
});
const addPointIfNeed = (points, pointProps, pointCreated) => {
let notCreatePoint = false;
if (pointCreated) {
notCreatePoint = pointCreated(pointProps)
}
if (!notCreatePoint) {
const point = createPoint(pointProps);
points.push(point)
}
};
function normalizeGroupingLoadOptions(group) {
if (!Array.isArray(group)) {
group = [group]
}
return group.map(((item, i) => {
if ((0, _type.isString)(item)) {
return {
selector: item,
isExpanded: i < group.length - 1
}
}
return item
}))
}
var _default = exports.default = {
renderNoDataText($element) {
const that = this;
$element = $element || this.element();
if (!$element) {
return
}
const noDataClass = that.addWidgetPrefix("nodata");
let noDataElement = $element.find(`.${noDataClass}`).last();
const isVisible = this._dataController.isEmpty();
const isLoading = this._dataController.isLoading();
if (!noDataElement.length) {
noDataElement = (0, _renderer.default)("<span>").addClass(noDataClass)
}
if (!noDataElement.parent().is($element)) {
noDataElement.appendTo($element)
}
if (isVisible && !isLoading) {
noDataElement.removeClass("dx-hidden").text(that._getNoDataText())
} else {
noDataElement.addClass("dx-hidden")
}
},
renderLoadPanel($element, $container, isLocalStore) {
const that = this;
let loadPanelOptions;
that._loadPanel && that._loadPanel.$element().remove();
loadPanelOptions = that.option("loadPanel");
if (loadPanelOptions && ("auto" === loadPanelOptions.enabled ? !isLocalStore : loadPanelOptions.enabled)) {
loadPanelOptions = (0, _extend.extend)({
shading: false,
message: loadPanelOptions.text,
container: $container
}, loadPanelOptions);
that._loadPanel = that._createComponent((0, _renderer.default)("<div>").appendTo($container), _load_panel.default, loadPanelOptions)
} else {
that._loadPanel = null
}
},
calculateLoadPanelPosition($element) {
const $window = (0, _renderer.default)((0, _window.getWindow)());
if ((0, _size.getHeight)($element) > (0, _size.getHeight)($window)) {
return {
of: $window,
boundary: $element,
collision: "fit"
}
}
return {
of: $element
}
},
getIndexByKey(key, items, keyName) {
let index = -1;
if (void 0 !== key && Array.isArray(items)) {
keyName = arguments.length <= 2 ? "key" : keyName;
for (let i = 0; i < items.length; i++) {
const item = (0, _type.isDefined)(keyName) ? items[i][keyName] : items[i];
if ((0, _common.equalByValue)(key, item)) {
index = i;
break
}
}
}
return index
},
combineFilters(filters, operation) {
let resultFilter = [];
operation = operation || "and";
for (let i = 0; i < filters.length; i++) {
var _filters$i;
if (!filters[i]) {
continue
}
if (1 === (null === (_filters$i = filters[i]) || void 0 === _filters$i ? void 0 : _filters$i.length) && "!" === filters[i][0]) {
if ("and" === operation) {
return ["!"]
}
if ("or" === operation) {
continue
}
}
if (resultFilter.length) {
resultFilter.push(operation)
}
resultFilter.push(filters[i])
}
if (1 === resultFilter.length) {
resultFilter = resultFilter[0]
}
if (resultFilter.length) {
return resultFilter
}
return
},
checkChanges(changes, changeNames) {
let changesWithChangeNamesCount = 0;
for (let i = 0; i < changeNames.length; i++) {
if (changes[changeNames[i]]) {
changesWithChangeNamesCount++
}
}
return changes.length && changes.length === changesWithChangeNamesCount
},
equalFilterParameters: equalFilterParameters,
proxyMethod(instance, methodName, defaultResult) {
if (!instance[methodName]) {
instance[methodName] = function() {
const dataSource = this._dataSource;
return dataSource ? dataSource[methodName].apply(dataSource, arguments) : defaultResult
}
}
},
formatValue: formatValue,
getFormatOptionsByColumn: (column, target) => ({
format: column.format,
getDisplayFormat: column.getDisplayFormat,
customizeText: column.customizeText,
target: target,
trueText: column.trueText,
falseText: column.falseText
}),
getDisplayValue(column, value, data, rowType) {
if (column.displayValueMap && void 0 !== column.displayValueMap[value]) {
return column.displayValueMap[value]
}
if (column.calculateDisplayValue && data && "group" !== rowType) {
return column.calculateDisplayValue(data)
}
if (column.lookup && !("group" === rowType && (column.calculateGroupValue || column.calculateDisplayValue))) {
return column.lookup.calculateCellValue(value)
}
return value
},
getGroupRowSummaryText(summaryItems, summaryTexts) {
let result = "(";
for (let i = 0; i < summaryItems.length; i++) {
const summaryItem = summaryItems[i];
result += (i > 0 ? ", " : "") + getSummaryText(summaryItem, summaryTexts)
}
return result + ")"
},
getSummaryText: getSummaryText,
normalizeSortingInfo: normalizeSortingInfo,
getFormatByDataType(dataType) {
switch (dataType) {
case "date":
return "shortDate";
case "datetime":
return "shortDateShortTime";
default:
return
}
},
getHeaderFilterGroupParameters(column, remoteGrouping) {
let result = [];
const dataField = column.dataField || column.name;
const groupInterval = _filtering.default.getGroupInterval(column);
if (groupInterval) {
(0, _iterator.each)(groupInterval, ((index, interval) => {
result.push(remoteGrouping ? {
selector: dataField,
groupInterval: interval,
isExpanded: index < groupInterval.length - 1
} : getIntervalSelector.bind(column, interval))
}));
return result
}
if (remoteGrouping) {
result = [{
selector: dataField,
isExpanded: false
}]
} else {
result = function(data) {
let result = column.calculateCellValue(data);
if (void 0 === result || "" === result) {
result = null
}
return result
};
if (column.sortingMethod) {
result = [{
selector: result,
compare: column.sortingMethod.bind(column)
}]
}
}
return result
},
equalSortParameters(sortParameters1, sortParameters2, ignoreIsExpanded) {
sortParameters1 = normalizeSortingInfo(sortParameters1);
sortParameters2 = normalizeSortingInfo(sortParameters2);
if (Array.isArray(sortParameters1) && Array.isArray(sortParameters2)) {
if (sortParameters1.length !== sortParameters2.length) {
return false
}
for (let i = 0; i < sortParameters1.length; i++) {
if (!equalSelectors(sortParameters1[i].selector, sortParameters2[i].selector) || sortParameters1[i].desc !== sortParameters2[i].desc || sortParameters1[i].groupInterval !== sortParameters2[i].groupInterval || !ignoreIsExpanded && Boolean(sortParameters1[i].isExpanded) !== Boolean(sortParameters2[i].isExpanded)) {
return false
}
}
return true
}
return (!sortParameters1 || !sortParameters1.length) === (!sortParameters2 || !sortParameters2.length)
},
getPointsByColumns(items, pointCreated) {
let isVertical = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : false;
let startColumnIndex = arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : 0;
let needToCheckPrevPoint = arguments.length > 4 && void 0 !== arguments[4] ? arguments[4] : false;
const result = [];
const cellsLength = items.length;
let $item;
let offset = {
left: 0,
top: 0
};
let itemRect = {
width: 0,
height: 0
};
let columnIndex = startColumnIndex;
let rtlEnabled;
for (let i = 0; i <= cellsLength; i++) {
var _$item;
if (i < cellsLength) {
$item = items.eq(i);
offset = $item.offset();
itemRect = (0, _position.getBoundingRect)($item.get(0));
rtlEnabled = "rtl" === $item.css("direction")
}
const offsetRight = offset.left + itemRect.width;
const offsetBottom = offset.top + itemRect.height;
const pointProps = {
index: columnIndex,
columnIndex: columnIndex,
item: null === (_$item = $item) || void 0 === _$item ? void 0 : _$item.get(0),
x: !isVertical && rtlEnabled !== (i === cellsLength) ? offsetRight : offset.left,
y: isVertical && i === cellsLength ? offsetBottom : offset.top
};
if (!isVertical && i > 0) {
const prevItemOffset = items.eq(i - 1).offset();
const {
width: prevItemWidth
} = (0, _position.getBoundingRect)(items[i - 1]);
const prevItemOffsetX = rtlEnabled ? prevItemOffset.left : prevItemOffset.left + prevItemWidth;
if (prevItemOffset.top < pointProps.y) {
pointProps.y = prevItemOffset.top
}
if (needToCheckPrevPoint && Math.round(prevItemOffsetX) !== Math.round(pointProps.x)) {
const prevPointProps = _extends({}, pointProps, {
item: items[i - 1],
x: prevItemOffsetX
});
if (rtlEnabled) {
pointProps.isRightBoundary = true;
prevPointProps.isLeftBoundary = true
} else {
pointProps.isLeftBoundary = true;
prevPointProps.isRightBoundary = true
}
addPointIfNeed(result, prevPointProps, pointCreated)
}
}
addPointIfNeed(result, pointProps, pointCreated);
columnIndex++
}
return result
},
getExpandCellTemplate: () => ({
allowRenderToDetachedContainer: true,
render(container, options) {
const $container = (0, _renderer.default)(container);
if ((0, _type.isDefined)(options.value) && !(options.data && options.data.isContinuation) && !options.row.isNewRow) {
const rowsView = options.component.getView("rowsView");
$container.addClass("dx-datagrid-expand").addClass("dx-selection-disabled");
(0, _renderer.default)("<div>").addClass(options.value ? "dx-datagrid-group-opened" : "dx-datagrid-group-closed").appendTo($container);
rowsView.setAria("label", options.value ? rowsView.localize("dxDataGrid-ariaCollapse") : rowsView.localize("dxDataGrid-ariaExpand"), $container)
} else {
setEmptyText($container)
}
}
}),
setEmptyText: setEmptyText,
isDateType: isDateType,
getSelectionRange(focusedElement) {
try {
if (focusedElement) {
return {
selectionStart: focusedElement.selectionStart,
selectionEnd: focusedElement.selectionEnd
}
}
} catch (e) {}
return {}
},
setSelectionRange(focusedElement, selectionRange) {
try {
if (focusedElement && focusedElement.setSelectionRange) {
focusedElement.setSelectionRange(selectionRange.selectionStart, selectionRange.selectionEnd)
}
} catch (e) {}
},
focusAndSelectElement(component, $element) {
const isFocused = $element.is(":focus");
_events_engine.default.trigger($element, "focus");
const isSelectTextOnEditingStart = component.option("editing.selectTextOnEditStart");
const element = $element.get(0);
if (!isFocused && isSelectTextOnEditingStart && $element.is(".dx-texteditor-input") && !$element.is("[readonly]")) {
const editor = getWidgetInstance($element.closest(".dx-texteditor"));
(0, _deferred.when)(editor && editor._loadItemDeferred).done((() => {
element.select()
}))
}
},
getWidgetInstance: getWidgetInstance,
getLastResizableColumnIndex(columns, resultWidths) {
const hasResizableColumns = columns.some((column => column && !column.command && !column.fixed && false !== column.allowResizing));
let lastColumnIndex;
for (lastColumnIndex = columns.length - 1; columns[lastColumnIndex]; lastColumnIndex--) {
const column = columns[lastColumnIndex];
const width = resultWidths && resultWidths[lastColumnIndex];
const allowResizing = !hasResizableColumns || false !== column.allowResizing;
if (!column.command && !column.fixed && "adaptiveHidden" !== width && allowResizing) {
break
}
}
return lastColumnIndex
},
isElementInCurrentGrid(controller, $element) {
if ($element && $element.length) {
const $grid = $element.closest(`.${controller.getWidgetContainerClass()}`).parent();
return $grid.is(controller.component.$element())
}
return false
},
isVirtualRowRendering(that) {
const rowRenderingMode = that.option(ROW_RENDERING_MODE_OPTION);
const isVirtualMode = "virtual" === that.option("scrolling.mode");
const isAppendMode = "infinite" === that.option("scrolling.mode");
if (false === that.option(LEGACY_SCROLLING_MODE) && (isVirtualMode || isAppendMode)) {
return true
}
return "virtual" === rowRenderingMode
},
getPixelRatio: window => window.devicePixelRatio || 1,
getContentHeightLimit(browser) {
if (browser.mozilla) {
return 8e6
}
return 15e6 / this.getPixelRatio((0, _window.getWindow)())
},
normalizeLookupDataSource(lookup) {
let lookupDataSourceOptions;
if (lookup.items) {
lookupDataSourceOptions = lookup.items
} else {
lookupDataSourceOptions = lookup.dataSource;
if ((0, _type.isFunction)(lookupDataSourceOptions) && !_variable_wrapper.default.isWrapped(lookupDataSourceOptions)) {
lookupDataSourceOptions = lookupDataSourceOptions({})
}
}
return (0, _utils.normalizeDataSourceOptions)(lookupDataSourceOptions)
},
getWrappedLookupDataSource(column, dataSource, filter) {
if (!dataSource) {
return []
}
const lookupDataSourceOptions = this.normalizeLookupDataSource(column.lookup);
if (column.calculateCellValue !== column.defaultCalculateCellValue) {
return lookupDataSourceOptions
}
const hasGroupPaging = dataSource.remoteOperations().groupPaging;
const hasLookupOptimization = column.displayField && (0, _type.isString)(column.displayField);
let cachedUniqueRelevantItems;
let previousTake;
let previousSkip;
const sliceItems = (items, loadOptions) => {
const start = loadOptions.skip ?? 0;
const end = loadOptions.take ? start + loadOptions.take : items.length;
return items.slice(start, end)
};
const lookupDataSource = _extends({}, lookupDataSourceOptions, {
__dataGridSourceFilter: filter,
load: loadOptions => {
const d = new _deferred.Deferred;
(loadOptions => {
const group = normalizeGroupingLoadOptions(hasLookupOptimization ? [column.dataField, column.displayField] : column.dataField);
const d = new _deferred.Deferred;
const canUseCache = cachedUniqueRelevantItems && (!hasGroupPaging || loadOptions.skip === previousSkip && loadOptions.take === previousTake);
if (canUseCache) {
d.resolve(sliceItems(cachedUniqueRelevantItems, loadOptions))
} else {
previousSkip = loadOptions.skip;
previousTake = loadOptions.take;
dataSource.load({
filter: filter,
group: group,
take: hasGroupPaging ? loadOptions.take : void 0,
skip: hasGroupPaging ? loadOptions.skip : void 0
}).done((items => {
cachedUniqueRelevantItems = items;
d.resolve(hasGroupPaging ? items : sliceItems(items, loadOptions))
})).fail(d.fail)
}
return d
})(loadOptions).done((items => {
if (0 === items.length) {
d.resolve([]);
return
}
const filter = this.combineFilters(items.flatMap((data => data.key)).map((key => [column.lookup.valueExpr, key])), "or");
const newDataSource = new _data_source.default(_extends({}, lookupDataSourceOptions, loadOptions, {
filter: this.combineFilters([filter, loadOptions.filter], "and"),
paginate: false
}));
newDataSource.load().done(d.resolve).fail(d.fail)
})).fail(d.fail);
return d
},
key: column.lookup.valueExpr,
byKey(key) {
const d = (0, _deferred.Deferred)();
this.load({
filter: [column.lookup.valueExpr, "=", key]
}).done((arr => {
d.resolve(arr[0])
}));
return d.promise()
}
});
return lookupDataSource
},
logHeaderFilterDeprecatedWarningIfNeed(component) {
const logWarning = component._logDeprecatedOptionWarning.bind(component);
if ((0, _type.isDefined)(component.option("headerFilter.allowSearch"))) {
logWarning("headerFilter.allowSearch", {
since: "23.1",
alias: "headerFilter.search.enabled"
})
}
if ((0, _type.isDefined)(component.option("headerFilter.searchTimeout"))) {
logWarning("headerFilter.searchTimeout", {
since: "23.1",
alias: "headerFilter.search.timeout"
})
}
const specificName = "dxPivotGrid" === component.NAME ? "dataSource.fields" : "columns";
const columns = component.option(specificName);
if (!Array.isArray(columns)) {
return
}
const logSpecificDeprecatedWarningIfNeed = columns => {
columns.forEach((column => {
var _column$columns;
const headerFilter = column.headerFilter || {};
if ((0, _type.isDefined)(headerFilter.allowSearch)) {
logWarning(`${specificName}[].headerFilter.allowSearch`, {
since: "23.1",
alias: `${specificName}[].headerFilter.search.enabled`
})
}
if ((0, _type.isDefined)(headerFilter.searchMode)) {
logWarning(`${specificName}[].headerFilter.searchMode`, {
since: "23.1",
alias: `${specificName}[].headerFilter.search.mode`
})
}
if (null !== (_column$columns = column.columns) && void 0 !== _column$columns && _column$columns.length) {
logSpecificDeprecatedWarningIfNeed(column.columns)
}
}))
};
logSpecificDeprecatedWarningIfNeed(columns)
},
getComponentBorderWidth(that, $rowsViewElement) {
const borderWidth = that.option("showBorders") ? Math.ceil((0, _size.getOuterWidth)($rowsViewElement) - (0, _size.getInnerWidth)($rowsViewElement)) : 0;
return borderWidth
},
isCustomCommandColumn(columns, commandColumn) {
const customCommandColumns = columns.filter((column => column.type === commandColumn.type));
return !!customCommandColumns.length
}
};