');
+ var lastColIndex = -1;
+ var th;
+
+ var gapWidth;
+ var skippedCount;
+ var emptyColspan;
+
+ var groupNameAdded = false;
+
+ // Check every column if it has an aggregation value
+ for (var [colIndex, aggValue] of Object.entries(aggregation.values)) {
+ if (aggValue !== null) {
+ colIndex = parseInt(colIndex); // Convert colIndex to integer
+
+ // If there is a gap between columns with aggregation values, append empty th
+ if ((lastColIndex + 1) !== colIndex) {
+ gapWidth = colIndex - lastColIndex - 1;
+ skippedCount = aggregation.skipped.filter(index => index > lastColIndex && index < colIndex).length;
+ emptyColspan = gapWidth - skippedCount;
+
+ if (emptyColspan > 0) {
+ th = $('
').attr('scope', 'row');
+ // Adding colspan only if emptyColspan greater than 1, otherwise it's redundant
+ if (emptyColspan > 1) {
+ th.attr('colspan', emptyColspan);
+ }
+ // Add group name to first available th element
+ if (!groupNameAdded) {
+ th.append(display);
+ groupNameAdded = true;
+ }
+
+ row.append(th);
+ }
+ }
+
+ // Append the aggregation value
+ row.append(
+ $('
').attr('scope', 'row').css({
+ 'text-align': this.c.aggTextAlign,
+ 'padding-left': this.c.aggPaddingLeft,
+ 'padding-right': this.c.aggPaddingRight,
+ }).append(aggValue)
+ );
+
+ lastColIndex = colIndex;
+ }
+ }
+
+ // Append trailing th element if needed at the end
+ if (lastColIndex < dt.columns()[0].length - 1) {
+ gapWidth = dt.columns.length - lastColIndex - 1;
+ skippedCount = aggregation.skipped.filter(index => index > lastColIndex).length;
+ emptyColspan = gapWidth - skippedCount;
+
+ if (emptyColspan > 0) {
+ th = $('
').attr('scope', 'row');
+ // Adding colspan only if emptyColspan greater than 1, otherwise it's redundant
+ if (emptyColspan > 1) {
+ th.attr('colspan', emptyColspan);
+ }
+ // Add group name to th element if not already added
+ if (!groupNameAdded) {
+ th.append(display);
+ groupNameAdded = true;
+ }
+
+ row.append(th);
+ }
+ }
+ }
else {
row = $('
').append(
$('
').attr('colspan', this._colspan()).attr('scope', 'row').append(display)
@@ -308,6 +485,74 @@ $.extend(RowGroup.prototype, {
.addClass(this.c.className)
.addClass(className)
.addClass('dtrg-level-' + level);
+ },
+
+ /**
+ * Get the aggregation result for a data set (index) of rows
+ * @param {DataTables.Api} rows API of the rows to consider for this aggregation
+ * @param {string|function} aggFunc name of aggregation function or custom function provided by the user
+ * @param {number} colIndex index of the column to aggregate
+ * @param {number} decimalPlaces number of decimal places to round to
+ * @param {boolean} showTrailingZeros whether to show trailing zeros in the result
+ * @returns {number|*} result of the aggregation
+ * @private
+ */
+ _aggregateData: function(rows, aggFunc, colIndex, decimalPlaces = 2, showTrailingZeros = false) {
+ var dt = this.s.dt;
+ var columnKey = dt.settings()[0].aoColumns[colIndex].data;
+
+ var data = dt.rows(rows).data().toArray();
+ var result;
+
+ if (typeof aggFunc === 'function') {
+ // User defined aggregation function, can return any value
+ result = aggFunc( data.map((row) => row[columnKey]) );
+ } else {
+ // Predefined aggregation functions
+ switch (aggFunc) {
+ case 'avg':
+ var sum = data.reduce((acc, row) => {
+ var value = parseFloat(row[columnKey]);
+ return acc + (isNaN(value) ? 0 : value);
+ }, 0);
+ result = sum / data.length;
+ break;
+ case 'sum':
+ result = data.reduce((acc, row) => {
+ var value = parseFloat(row[columnKey]);
+ return acc + (isNaN(value) ? 0 : value);
+ }, 0);
+ break;
+ case 'count':
+ result = data.length;
+ break;
+ case 'max':
+ result = data.reduce((acc, row) => {
+ var value = parseFloat(row[columnKey]);
+ if (isNaN(value)) return acc;
+ return acc === null || value > acc ? value : acc;
+ }, null);
+ break;
+ case 'min':
+ result = data.reduce((acc, row) => {
+ var value = parseFloat(row[columnKey]);
+ if (isNaN(value)) return acc;
+ return acc === null || value < acc ? value : acc;
+ }, null);
+ break;
+ default:
+ result = null;
+ }
+ }
+
+ if (!isNaN(parseFloat(result))) {
+ result = parseFloat(result).toFixed(decimalPlaces);
+ if (!showTrailingZeros) {
+ result = parseFloat(result);
+ }
+ }
+
+ return result;
}
});
@@ -368,10 +613,40 @@ RowGroup.defaults = {
*/
startRender: function (rows, group) {
return group;
- }
+ },
+
+ /**
+ * Grouping aggregation left padding value
+ * @type string
+ */
+ aggPaddingLeft: '10px',
+
+ /**
+ * Grouping aggregation right padding value
+ * @type string
+ */
+ aggPaddingRight: '10px',
+
+ /**
+ * Grouping aggregation default text alignment - can be redefined for each column as well
+ * @type string
+ */
+ aggTextAlign: 'center',
+
+ /**
+ * Grouping aggregation default decimal places count - can be redefined for each column as well
+ * @type string|integer
+ */
+ aggDecimalPlaces: 2,
+
+ /**
+ * Defines whether the aggregated values should show trailing zeros or not - can be redefined for each column as well
+ * @type boolean
+ */
+ aggShowTrailingZeros: false
};
-RowGroup.version = '1.5.0';
+RowGroup.version = '1.6.0';
$.fn.dataTable.RowGroup = RowGroup;
$.fn.DataTable.RowGroup = RowGroup;