UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

466 lines (460 loc) • 17.6 kB
/** * DevExtreme (esm/ui/pivot_grid/ui.pivot_grid.summary_display_modes.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 { isFunction, isDefined, isObject } from "../../core/utils/type"; import { extend } from "../../core/utils/extend"; import { inArray } from "../../core/utils/array"; import { findField, foreachTree, setFieldProperty } from "./ui.pivot_grid.utils"; var COLUMN = "column"; var ROW = "row"; var NULL = null; var calculatePercentValue = function(value, totalValue) { var result = value / totalValue; if (!isDefined(value) || isNaN(result)) { result = NULL } return result }; var _percentOfGrandTotal = function(e, dimension) { return calculatePercentValue(e.value(), e.grandTotal(dimension).value()) }; var percentOfParent = function(e, dimension) { var parent = e.parent(dimension); var parentValue = parent ? parent.value() : e.value(); return calculatePercentValue(e.value(), parentValue) }; var createAbsoluteVariationExp = function(allowCrossGroup) { return function(e) { var prevCell = e.prev(COLUMN, allowCrossGroup); var prevValue = prevCell && prevCell.value(); if (isDefined(prevValue) && isDefined(e.value())) { return e.value() - prevValue } return NULL } }; var createPercentVariationExp = function(allowCrossGroup) { var absoluteExp = createAbsoluteVariationExp(allowCrossGroup); return function(e) { var absVar = absoluteExp(e); var prevCell = e.prev(COLUMN, allowCrossGroup); var prevValue = prevCell && prevCell.value(); return absVar !== NULL && prevValue ? absVar / prevValue : NULL } }; var summaryDictionary = { percentOfColumnTotal: function(e) { return percentOfParent(e, ROW) }, percentOfRowTotal: function(e) { return percentOfParent(e, COLUMN) }, percentOfColumnGrandTotal: function(e) { return _percentOfGrandTotal(e, ROW) }, percentOfRowGrandTotal: function(e) { return _percentOfGrandTotal(e, COLUMN) }, percentOfGrandTotal: function(e) { return _percentOfGrandTotal(e) } }; var getPrevCellCrossGroup = function getPrevCellCrossGroup(cell, direction) { if (!cell || !cell.parent(direction)) { return } var prevCell = cell.prev(direction); if (!prevCell) { prevCell = getPrevCellCrossGroup(cell.parent(direction), direction) } return prevCell }; var createRunningTotalExpr = function(field) { if (!field.runningTotal) { return } var direction = field.runningTotal === COLUMN ? ROW : COLUMN; return function(e) { var prevCell = field.allowCrossGroupCalculation ? getPrevCellCrossGroup(e, direction) : e.prev(direction, false); var value = e.value(true); var prevValue = prevCell && prevCell.value(true); if (isDefined(prevValue) && isDefined(value)) { value = prevValue + value } else if (isDefined(prevValue)) { value = prevValue } return value } }; function createCache() { return { fields: {}, positions: {} } } function getFieldPos(descriptions, field, cache) { var fieldParams = { index: -1 }; if (!isObject(field)) { if (cache.fields[field]) { field = cache[field] } else { var allFields = descriptions.columns.concat(descriptions.rows).concat(descriptions.values); var fieldIndex = findField(allFields, field); field = cache[field] = allFields[fieldIndex] } } if (field) { var area = field.area || "data"; fieldParams = cache.positions[field.index] = cache.positions[field.index] || { area: area, index: inArray(field, descriptions["data" === area ? "values" : area + "s"]) } } return fieldParams } function getPathFieldName(dimension) { return dimension === ROW ? "_rowPath" : "_columnPath" } var SummaryCell = function(columnPath, rowPath, data, descriptions, fieldIndex, fieldsCache) { this._columnPath = columnPath; this._rowPath = rowPath; this._fieldIndex = fieldIndex; this._fieldsCache = fieldsCache || createCache(); this._data = data; this._descriptions = descriptions; var cell = data.values && data.values[rowPath[0].index] && data.values[rowPath[0].index][columnPath[0].index]; if (cell) { cell.originalCell = cell.originalCell || cell.slice(); cell.postProcessedFlags = cell.postProcessedFlags || []; this._cell = cell } }; SummaryCell.prototype = extend(SummaryCell.prototype, { _getPath: function(dimension) { return this[getPathFieldName(dimension)] }, _getDimension: function(dimension) { dimension = dimension === ROW ? "rows" : "columns"; return this._descriptions[dimension] }, _createCell: function(config) { return new SummaryCell(config._columnPath || this._columnPath, config._rowPath || this._rowPath, this._data, this._descriptions, this._fieldIndex) }, parent: function(direction) { var path = this._getPath(direction).slice(); var config = {}; path.shift(); if (path.length) { config[getPathFieldName(direction)] = path; return this._createCell(config) } return NULL }, children: function(direction) { var path = this._getPath(direction).slice(); var item = path[0]; var result = []; var cellConfig = {}; if (item.children) { for (var i = 0; i < item.children.length; i++) { cellConfig[getPathFieldName(direction)] = [item.children[i]].concat(path.slice()); result.push(this._createCell(cellConfig)) } } return result }, grandTotal: function(direction) { var config = {}; var rowPath = this._rowPath; var columnPath = this._columnPath; var dimensionPath = this._getPath(direction); var pathFieldName = getPathFieldName(direction); if (!direction) { config._rowPath = [rowPath[rowPath.length - 1]]; config._columnPath = [columnPath[columnPath.length - 1]] } else { config[pathFieldName] = [dimensionPath[dimensionPath.length - 1]] } return this._createCell(config) }, next: function(direction, allowCrossGroup) { var currentPath = this._getPath(direction); var item = currentPath[0]; var parent = this.parent(direction); var siblings; if (parent) { var index = inArray(item, currentPath[1].children); siblings = parent.children(direction); if (siblings[index + 1]) { return siblings[index + 1] } } if (allowCrossGroup && parent) { do { parent = parent.next(direction, allowCrossGroup); siblings = parent ? parent.children(direction) : [] } while (parent && !siblings.length); return siblings[0] || NULL } return NULL }, prev: function(direction, allowCrossGroup) { var currentPath = this._getPath(direction); var item = currentPath[0]; var parent = this.parent(direction); var siblings; if (parent) { var index = inArray(item, currentPath[1].children); siblings = parent.children(direction); if (siblings[index - 1]) { return siblings[index - 1] } } if (allowCrossGroup && parent) { do { parent = parent.prev(direction, allowCrossGroup); siblings = parent ? parent.children(direction) : [] } while (parent && !siblings.length); return siblings[siblings.length - 1] || NULL } return NULL }, cell: function() { return this._cell }, field: function(area) { if ("data" === area) { return this._descriptions.values[this._fieldIndex] } var path = this._getPath(area); var descriptions = this._getDimension(area); var field = descriptions[path.length - 2]; return field || NULL }, child: function(direction, fieldValue) { var children = this.children(direction); for (var i = 0; i < children.length; i++) { var childLevelField = childLevelField || children[i].field(direction); if (children[i].value(childLevelField) === fieldValue) { return children[i] } } return NULL }, slice: function(field, value) { var config = {}; var fieldPos = getFieldPos(this._descriptions, field, this._fieldsCache); var area = fieldPos.area; var fieldIndex = fieldPos.index; var sliceCell = NULL; if (area === ROW || area === COLUMN) { var path = this._getPath(area).slice(); var level = -1 !== fieldIndex && path.length - 2 - fieldIndex; if (path[level]) { [][path.length - 1] = path[path.length - 1]; for (var i = level; i >= 0; i--) { if (path[i + 1]) { var childItems = path[i + 1].children || []; var currentValue = i === level ? value : path[i].value; path[i] = void 0; for (var childIndex = 0; childIndex < childItems.length; childIndex++) { if (childItems[childIndex].value === currentValue) { path[i] = childItems[childIndex]; break } } } if (void 0 === path[i]) { return sliceCell } } config[getPathFieldName(area)] = path; sliceCell = this._createCell(config) } } return sliceCell }, value: function(arg1, arg2) { var cell = this._cell; var fieldIndex = this._fieldIndex; var fistArgIsBoolean = true === arg1 || false === arg1; var field = !fistArgIsBoolean ? arg1 : NULL; var needCalculatedValue = fistArgIsBoolean && arg1 || arg2; if (isDefined(field)) { var fieldPos = getFieldPos(this._descriptions, field, this._fieldsCache); fieldIndex = fieldPos.index; if ("data" !== fieldPos.area) { var path = this._getPath(fieldPos.area); var level = -1 !== fieldIndex && path.length - 2 - fieldIndex; return path[level] && path[level].value } } if (cell && cell.originalCell) { return needCalculatedValue ? cell[fieldIndex] : cell.originalCell[fieldIndex] } return NULL }, isPostProcessed(field) { var fieldIndex = this._fieldIndex; if (isDefined(field)) { var fieldPos = getFieldPos(this._descriptions, field, this._fieldsCache); fieldIndex = fieldPos.index; if ("data" !== fieldPos.area) { return false } } return !!(this._cell && this._cell.postProcessedFlags[fieldIndex]) } }); function getExpression(field) { var summaryDisplayMode = field.summaryDisplayMode; var crossGroupCalculation = field.allowCrossGroupCalculation; var expression = NULL; if (isFunction(field.calculateSummaryValue)) { expression = field.calculateSummaryValue } else if (summaryDisplayMode) { if ("absoluteVariation" === summaryDisplayMode) { expression = createAbsoluteVariationExp(crossGroupCalculation) } else if ("percentVariation" === summaryDisplayMode) { expression = createPercentVariationExp(crossGroupCalculation) } else { expression = summaryDictionary[summaryDisplayMode] } if (expression && !field.format && -1 !== summaryDisplayMode.indexOf("percent")) { setFieldProperty(field, "format", "percent") } } return expression } function processDataCell(data, rowIndex, columnIndex, isRunningTotalCalculation) { var values = data.values[rowIndex][columnIndex] = data.values[rowIndex][columnIndex] || []; var originalCell = values.originalCell; if (!originalCell) { return } if (values.allowResetting || !isRunningTotalCalculation) { data.values[rowIndex][columnIndex] = originalCell.slice() } data.values[rowIndex][columnIndex].allowResetting = isRunningTotalCalculation } export function applyDisplaySummaryMode(descriptions, data) { var expressions = []; var columnElements = [{ index: data.grandTotalColumnIndex, children: data.columns }]; var rowElements = [{ index: data.grandTotalRowIndex, children: data.rows }]; var valueFields = descriptions.values; var fieldsCache = createCache(); data.values = data.values || []; foreachTree(columnElements, columnPath => { columnPath[0].isEmpty = [] }, false); foreachTree(rowElements, (function(rowPath) { var rowItem = rowPath[0]; rowItem.isEmpty = []; data.values[rowItem.index] = data.values[rowItem.index] || []; foreachTree(columnElements, (function(columnPath) { var columnItem = columnPath[0]; var isEmptyCell; processDataCell(data, rowItem.index, columnItem.index, false); for (var i = 0; i < valueFields.length; i++) { var field = valueFields[i]; var expression = expressions[i] = void 0 === expressions[i] ? getExpression(field) : expressions[i]; isEmptyCell = false; if (expression) { var expressionArg = new SummaryCell(columnPath, rowPath, data, descriptions, i, fieldsCache); var cell = expressionArg.cell(); var value = cell[i] = expression(expressionArg); cell.postProcessedFlags[i] = true; isEmptyCell = null === value || void 0 === value } if (void 0 === columnItem.isEmpty[i]) { columnItem.isEmpty[i] = true } if (void 0 === rowItem.isEmpty[i]) { rowItem.isEmpty[i] = true } if (!isEmptyCell) { rowItem.isEmpty[i] = columnItem.isEmpty[i] = false } } }), false) }), false); data.isEmptyGrandTotalRow = rowElements[0].isEmpty; data.isEmptyGrandTotalColumn = columnElements[0].isEmpty } export function applyRunningTotal(descriptions, data) { var expressions = []; var columnElements = [{ index: data.grandTotalColumnIndex, children: data.columns }]; var rowElements = [{ index: data.grandTotalRowIndex, children: data.rows }]; var valueFields = descriptions.values; var fieldsCache = createCache(); data.values = data.values || []; foreachTree(rowElements, (function(rowPath) { var rowItem = rowPath[0]; data.values[rowItem.index] = data.values[rowItem.index] || []; foreachTree(columnElements, (function(columnPath) { var columnItem = columnPath[0]; processDataCell(data, rowItem.index, columnItem.index, true); for (var i = 0; i < valueFields.length; i++) { var field = valueFields[i]; var expression = expressions[i] = void 0 === expressions[i] ? createRunningTotalExpr(field) : expressions[i]; if (expression) { var expressionArg = new SummaryCell(columnPath, rowPath, data, descriptions, i, fieldsCache); var cell = expressionArg.cell(); cell[i] = expression(expressionArg); cell.postProcessedFlags[i] = true } } }), false) }), false) } export function createMockSummaryCell(descriptions, fields, indices) { var summaryCell = new SummaryCell([], [], {}, descriptions, 0); summaryCell.value = function(fieldId) { if (isDefined(fieldId)) { var index = findField(fields, fieldId); var field = fields[index]; if (!indices[index] && field && !isDefined(field.area)) { descriptions.values.push(field); indices[index] = true } } }; summaryCell.grandTotal = function() { return this }; summaryCell.children = function() { return [] }; return summaryCell }