@hrforte/react-pivottable 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PivotTable.js +72 -0
- package/dist/PivotTable.js.map +1 -0
- package/dist/PivotTableUI.js +1846 -0
- package/dist/PivotTableUI.js.map +1 -0
- package/dist/PlotlyRenderers.js +822 -0
- package/dist/PlotlyRenderers.js.map +1 -0
- package/dist/TableRenderers.js +1067 -0
- package/dist/TableRenderers.js.map +1 -0
- package/dist/Utilities.js +1476 -0
- package/dist/Utilities.js.map +1 -0
- package/dist/__tests__/Utilities-test.js +571 -0
- package/dist/__tests__/Utilities-test.js.map +1 -0
- package/dist/pivottable.css +377 -0
- package/package.json +4 -16
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
8
|
+
|
|
9
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
10
|
+
|
|
11
|
+
exports.applyCellFormatting = applyCellFormatting;
|
|
12
|
+
|
|
13
|
+
var _react = require('react');
|
|
14
|
+
|
|
15
|
+
var _react2 = _interopRequireDefault(_react);
|
|
16
|
+
|
|
17
|
+
var _propTypes = require('prop-types');
|
|
18
|
+
|
|
19
|
+
var _propTypes2 = _interopRequireDefault(_propTypes);
|
|
20
|
+
|
|
21
|
+
var _Utilities = require('./Utilities');
|
|
22
|
+
|
|
23
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
24
|
+
|
|
25
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
26
|
+
|
|
27
|
+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
28
|
+
|
|
29
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
30
|
+
|
|
31
|
+
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
|
32
|
+
|
|
33
|
+
// helper function for setting row/col-span in pivotTableRenderer
|
|
34
|
+
var spanSize = function spanSize(arr, i, j) {
|
|
35
|
+
var x = void 0;
|
|
36
|
+
if (i !== 0) {
|
|
37
|
+
var asc = void 0,
|
|
38
|
+
end = void 0;
|
|
39
|
+
var noDraw = true;
|
|
40
|
+
for (x = 0, end = j, asc = end >= 0; asc ? x <= end : x >= end; asc ? x++ : x--) {
|
|
41
|
+
if (arr[i - 1][x] !== arr[i][x]) {
|
|
42
|
+
noDraw = false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (noDraw) {
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
var len = 0;
|
|
50
|
+
while (i + len < arr.length) {
|
|
51
|
+
var asc1 = void 0,
|
|
52
|
+
end1 = void 0;
|
|
53
|
+
var stop = false;
|
|
54
|
+
for (x = 0, end1 = j, asc1 = end1 >= 0; asc1 ? x <= end1 : x >= end1; asc1 ? x++ : x--) {
|
|
55
|
+
if (arr[i][x] !== arr[i + len][x]) {
|
|
56
|
+
stop = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (stop) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
len++;
|
|
63
|
+
}
|
|
64
|
+
return len;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// ==> Calculate the positions for GroupSegments <==
|
|
68
|
+
function buildHeaderGroupSegments(rowAttrs, rowGroupConfig) {
|
|
69
|
+
var attrToGroup = {};
|
|
70
|
+
(rowGroupConfig || []).forEach(function (_ref) {
|
|
71
|
+
var label = _ref.label,
|
|
72
|
+
match = _ref.match;
|
|
73
|
+
|
|
74
|
+
match.forEach(function (name) {
|
|
75
|
+
attrToGroup[name] = label;
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
var segments = [];
|
|
80
|
+
rowAttrs.forEach(function (name) {
|
|
81
|
+
// eslint-disable-next-line no-undefined
|
|
82
|
+
var group = attrToGroup[name] !== undefined ? attrToGroup[name] : null;
|
|
83
|
+
var last = segments[segments.length - 1];
|
|
84
|
+
if (group !== null && last && last.group === group) {
|
|
85
|
+
last.span += 1;
|
|
86
|
+
} else {
|
|
87
|
+
segments.push({ group: group, span: 1 });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return segments;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Style cho ô có nhãn nhóm trong group header
|
|
94
|
+
var groupLabelStyle = {
|
|
95
|
+
textAlign: 'center'
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
function redColorScaleGenerator(values) {
|
|
99
|
+
var min = Math.min.apply(Math, values);
|
|
100
|
+
var max = Math.max.apply(Math, values);
|
|
101
|
+
return function (x) {
|
|
102
|
+
// eslint-disable-next-line no-magic-numbers
|
|
103
|
+
var nonRed = 255 - Math.round(255 * (x - min) / (max - min));
|
|
104
|
+
return { backgroundColor: 'rgb(255,' + nonRed + ',' + nonRed + ')' };
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Conditional formatting evaluation function
|
|
109
|
+
function evaluateCondition(value, condition) {
|
|
110
|
+
if (!condition || !condition.type) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
var type = condition.type,
|
|
115
|
+
conditionValue = condition.value;
|
|
116
|
+
|
|
117
|
+
var numValue = typeof value === 'number' ? value : parseFloat(value);
|
|
118
|
+
var numConditionValue = typeof conditionValue === 'number' ? conditionValue : parseFloat(conditionValue);
|
|
119
|
+
|
|
120
|
+
switch (type) {
|
|
121
|
+
case 'greaterThan':
|
|
122
|
+
return !isNaN(numValue) && !isNaN(numConditionValue) && numValue > numConditionValue;
|
|
123
|
+
case 'lessThan':
|
|
124
|
+
return !isNaN(numValue) && !isNaN(numConditionValue) && numValue < numConditionValue;
|
|
125
|
+
case 'greaterThanOrEqual':
|
|
126
|
+
return !isNaN(numValue) && !isNaN(numConditionValue) && numValue >= numConditionValue;
|
|
127
|
+
case 'lessThanOrEqual':
|
|
128
|
+
return !isNaN(numValue) && !isNaN(numConditionValue) && numValue <= numConditionValue;
|
|
129
|
+
case 'equal':
|
|
130
|
+
if (typeof value === 'string' && typeof conditionValue === 'string') {
|
|
131
|
+
return value === conditionValue;
|
|
132
|
+
}
|
|
133
|
+
return !isNaN(numValue) && !isNaN(numConditionValue) && numValue === numConditionValue;
|
|
134
|
+
case 'notEqual':
|
|
135
|
+
if (typeof value === 'string' && typeof conditionValue === 'string') {
|
|
136
|
+
return value !== conditionValue;
|
|
137
|
+
}
|
|
138
|
+
return isNaN(numValue) || isNaN(numConditionValue) || numValue !== numConditionValue;
|
|
139
|
+
case 'empty':
|
|
140
|
+
// Check for null, undefined, empty string, whitespace-only strings, or NaN
|
|
141
|
+
if (value === null || value === undefined) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (typeof value === 'string') {
|
|
145
|
+
return value.trim() === '';
|
|
146
|
+
}
|
|
147
|
+
if (typeof value === 'number') {
|
|
148
|
+
return isNaN(value);
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
case 'notEmpty':
|
|
152
|
+
// Check that value is not null, undefined, empty/whitespace string, or NaN
|
|
153
|
+
if (value === null || value === undefined) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (typeof value === 'string') {
|
|
157
|
+
return value.trim() !== '';
|
|
158
|
+
}
|
|
159
|
+
if (typeof value === 'number') {
|
|
160
|
+
return !isNaN(value);
|
|
161
|
+
}
|
|
162
|
+
// Other types (boolean, object, etc.) are considered not empty
|
|
163
|
+
return true;
|
|
164
|
+
case 'contains':
|
|
165
|
+
// Convert both values to strings for comparison
|
|
166
|
+
// This handles cases where values are numbers (from aggregators) or conditionValue is parsed as number
|
|
167
|
+
var valueStr = String(value || '');
|
|
168
|
+
var conditionValueStr = String(conditionValue || '');
|
|
169
|
+
if (valueStr && conditionValueStr) {
|
|
170
|
+
return valueStr.toLowerCase().includes(conditionValueStr.toLowerCase());
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
case 'notContains':
|
|
174
|
+
// Convert both values to strings for comparison
|
|
175
|
+
// This handles cases where values are numbers (from aggregators) or conditionValue is parsed as number
|
|
176
|
+
var valueStr2 = String(value || '');
|
|
177
|
+
var conditionValueStr2 = String(conditionValue || '');
|
|
178
|
+
if (valueStr2 && conditionValueStr2) {
|
|
179
|
+
return !valueStr2.toLowerCase().includes(conditionValueStr2.toLowerCase());
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
default:
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Apply conditional formatting rules to a value
|
|
188
|
+
function getConditionalFormattingStyle(value, conditionalFormatting) {
|
|
189
|
+
if (!conditionalFormatting || !Array.isArray(conditionalFormatting.rules)) {
|
|
190
|
+
return {};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Evaluate rules in order, return first matching rule's style
|
|
194
|
+
var _iteratorNormalCompletion = true;
|
|
195
|
+
var _didIteratorError = false;
|
|
196
|
+
var _iteratorError = undefined;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
for (var _iterator = conditionalFormatting.rules[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
200
|
+
var rule = _step.value;
|
|
201
|
+
|
|
202
|
+
if (evaluateCondition(value, rule.condition)) {
|
|
203
|
+
// Return a copy of the style object to avoid mutations
|
|
204
|
+
return rule.style ? Object.assign({}, rule.style) : {};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} catch (err) {
|
|
208
|
+
_didIteratorError = true;
|
|
209
|
+
_iteratorError = err;
|
|
210
|
+
} finally {
|
|
211
|
+
try {
|
|
212
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
213
|
+
_iterator.return();
|
|
214
|
+
}
|
|
215
|
+
} finally {
|
|
216
|
+
if (_didIteratorError) {
|
|
217
|
+
throw _iteratorError;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return {};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Merge styles, with conditional formatting taking precedence
|
|
226
|
+
function mergeStyles() {
|
|
227
|
+
for (var _len = arguments.length, styles = Array(_len), _key = 0; _key < _len; _key++) {
|
|
228
|
+
styles[_key] = arguments[_key];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Object.assign.apply(Object, [{}].concat(_toConsumableArray(styles.filter(function (s) {
|
|
232
|
+
return s && (typeof s === 'undefined' ? 'undefined' : _typeof(s)) === 'object';
|
|
233
|
+
}))));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Helper function to check if a string contains special characters (like -, /, etc.)
|
|
237
|
+
// This prevents formatting of date strings or formatted strings
|
|
238
|
+
// Only pure numeric strings (with optional decimal point and minus sign) should be formatted
|
|
239
|
+
function containsSpecialCharacters(str) {
|
|
240
|
+
if (typeof str !== 'string') {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
var trimmed = str.trim();
|
|
244
|
+
// Check if string contains any special characters that indicate it's not a raw number
|
|
245
|
+
// Allow: digits, decimal point, minus sign at start, plus sign at start, spaces
|
|
246
|
+
// Disallow: hyphens in middle, slashes, and other special characters
|
|
247
|
+
var pureNumericPattern = /^-?\d*\.?\d+$/;
|
|
248
|
+
return !pureNumericPattern.test(trimmed);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Helper function to check if an aggregation should skip formatting
|
|
252
|
+
// Aggregations like Count, First, Last should not be formatted
|
|
253
|
+
// Aggregations like Sum, Average should be formatted
|
|
254
|
+
function shouldSkipFormattingForAggregation(aggregatorName) {
|
|
255
|
+
if (!aggregatorName) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
var name = aggregatorName.toLowerCase();
|
|
260
|
+
// Skip formatting for these aggregation types
|
|
261
|
+
var skipFormattingAggregations = ['count', 'count unique values', 'list unique values', 'first', 'last', 'minimum', 'maximum'];
|
|
262
|
+
return skipFormattingAggregations.includes(name);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Apply cell formatting rules to a value
|
|
266
|
+
function applyCellFormatting(value, cellFormatting, defaultFormatter) {
|
|
267
|
+
var aggregatorName = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
|
268
|
+
|
|
269
|
+
// Don't format if aggregation type should skip formatting (e.g., Count, First, Last)
|
|
270
|
+
// Only use defaultFormatter if value is numeric, otherwise return as-is
|
|
271
|
+
var isNumeric = typeof value === 'number' && !isNaN(value) && isFinite(value) || typeof value === 'string' && value.trim() !== '' && !containsSpecialCharacters(value) && !isNaN(parseFloat(value)) && isFinite(parseFloat(value));
|
|
272
|
+
if (shouldSkipFormattingForAggregation(aggregatorName)) {
|
|
273
|
+
if (isNumeric && defaultFormatter) {
|
|
274
|
+
return defaultFormatter(value);
|
|
275
|
+
}
|
|
276
|
+
// Return raw value for non-numeric values (dates, strings, etc.)
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// If no cellFormatting rules, always use default formatter to return to original format
|
|
281
|
+
// This ensures values return to their original format when cellFormatting is cleared
|
|
282
|
+
// Check for all possible cases: null, undefined, empty object, empty rules array
|
|
283
|
+
var hasFormattingRules = cellFormatting && (typeof cellFormatting === 'undefined' ? 'undefined' : _typeof(cellFormatting)) === 'object' && Array.isArray(cellFormatting.rules) && cellFormatting.rules.length > 0 && cellFormatting.rules.some(function (rule) {
|
|
284
|
+
return rule && rule.format;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (!hasFormattingRules) {
|
|
288
|
+
// When cellFormatting rules are cleared, return the raw value (not formatted)
|
|
289
|
+
// This ensures all cells (regular, Totals, Aggregation) return to their original unformatted state
|
|
290
|
+
// Return the raw numeric value for numbers, or the value as-is for other types
|
|
291
|
+
if (isNumeric) {
|
|
292
|
+
// Return raw numeric value without any formatting
|
|
293
|
+
if (typeof value === 'number') {
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
// For string numbers, return the parsed numeric value
|
|
297
|
+
return parseFloat(value);
|
|
298
|
+
}
|
|
299
|
+
// For non-numeric values (dates, strings, etc.), return as-is
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Don't format if it contains special characters (dates, formatted strings, etc.)
|
|
304
|
+
// Only check this when cellFormatting rules exist
|
|
305
|
+
if (containsSpecialCharacters(value)) {
|
|
306
|
+
return defaultFormatter ? defaultFormatter(value) : String(value);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Check if value is a number - only apply custom formatting to raw numbers
|
|
310
|
+
// Handle both number type and string numbers (e.g., "123.45")
|
|
311
|
+
// Exclude strings with special characters from formatting
|
|
312
|
+
var numericValue = value;
|
|
313
|
+
|
|
314
|
+
// Convert string numbers to actual numbers for formatting
|
|
315
|
+
if (isNumeric && typeof value === 'string') {
|
|
316
|
+
numericValue = parseFloat(value);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Use the first rule (all rules apply to all values)
|
|
320
|
+
var matchingRule = cellFormatting.rules[0];
|
|
321
|
+
|
|
322
|
+
// If we found a matching rule and value is numeric, apply its formatting
|
|
323
|
+
if (matchingRule && matchingRule.format && isNumeric) {
|
|
324
|
+
var format = matchingRule.format;
|
|
325
|
+
var formatter = (0, _Utilities.numberFormat)({
|
|
326
|
+
digitsAfterDecimal: 'decimalPlaces' in format ? format.decimalPlaces : 2,
|
|
327
|
+
thousandsSep: 'thousandsSep' in format ? format.thousandsSep : ',',
|
|
328
|
+
decimalSep: 'decimalSep' in format ? format.decimalSep : '.',
|
|
329
|
+
prefix: 'prefix' in format ? format.prefix : '',
|
|
330
|
+
suffix: 'suffix' in format ? format.suffix : '',
|
|
331
|
+
showOriginal: 'showOriginal' in format ? format.showOriginal : false
|
|
332
|
+
});
|
|
333
|
+
return formatter(numericValue);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Not numeric or no matching rule, use default formatter
|
|
337
|
+
return defaultFormatter ? defaultFormatter(value) : String(value);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function makeRenderer() {
|
|
341
|
+
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
342
|
+
|
|
343
|
+
var TableRenderer = function (_React$PureComponent) {
|
|
344
|
+
_inherits(TableRenderer, _React$PureComponent);
|
|
345
|
+
|
|
346
|
+
function TableRenderer() {
|
|
347
|
+
_classCallCheck(this, TableRenderer);
|
|
348
|
+
|
|
349
|
+
return _possibleConstructorReturn(this, (TableRenderer.__proto__ || Object.getPrototypeOf(TableRenderer)).apply(this, arguments));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_createClass(TableRenderer, [{
|
|
353
|
+
key: 'render',
|
|
354
|
+
value: function render() {
|
|
355
|
+
var _this2 = this;
|
|
356
|
+
|
|
357
|
+
var pivotData = new _Utilities.PivotData(this.props);
|
|
358
|
+
var colAttrs = pivotData.props.cols;
|
|
359
|
+
var rowAttrs = pivotData.props.rows;
|
|
360
|
+
var aggregations = pivotData.getAggregations();
|
|
361
|
+
var primaryAggregation = pivotData.getPrimaryAggregation();
|
|
362
|
+
var hasMultipleAggregators = aggregations.length > 1;
|
|
363
|
+
var tableOptions = this.props.tableOptions || {};
|
|
364
|
+
var aggregationDisplayMode = tableOptions.aggregationDisplayMode === 'column' ? 'column' : 'row';
|
|
365
|
+
var isColumnAggregationMode = aggregationDisplayMode === 'column' && hasMultipleAggregators;
|
|
366
|
+
var rowAttrsToRender = hasMultipleAggregators && !isColumnAggregationMode ? rowAttrs.concat(['Values']) : rowAttrs;
|
|
367
|
+
var colAttrsToRender = colAttrs;
|
|
368
|
+
var hasColumnAttributes = colAttrsToRender.length > 0;
|
|
369
|
+
var showTotalsColumn = !isColumnAggregationMode;
|
|
370
|
+
var shouldRenderRowHeaderPlaceholder = rowAttrsToRender.length !== 0;
|
|
371
|
+
|
|
372
|
+
// --- Row group header (rowGroupConfig) ---
|
|
373
|
+
var rowGroupConfig = this.props.rowGroupConfig;
|
|
374
|
+
var hasHeaderGroup = Boolean(rowGroupConfig && rowGroupConfig.length);
|
|
375
|
+
var headerGroupSegments = hasHeaderGroup ? buildHeaderGroupSegments(rowAttrs, rowGroupConfig) : [];
|
|
376
|
+
// extraSpan accounts for the synthetic "Values" column added when there
|
|
377
|
+
// are multiple aggregators shown as rows; it isn't part of any group.
|
|
378
|
+
var headerGroupExtraSpan = rowAttrsToRender.length - rowAttrs.length;
|
|
379
|
+
var renderGroupSegments = function renderGroupSegments(rowSpan) {
|
|
380
|
+
var extraSpan = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
381
|
+
|
|
382
|
+
var cells = headerGroupSegments.map(function (seg, idx) {
|
|
383
|
+
return _react2.default.createElement(
|
|
384
|
+
'th',
|
|
385
|
+
{
|
|
386
|
+
key: 'hgSeg' + idx,
|
|
387
|
+
colSpan: seg.span,
|
|
388
|
+
rowSpan: rowSpan
|
|
389
|
+
// eslint-disable-next-line no-undefined
|
|
390
|
+
, style: seg.group ? groupLabelStyle : undefined
|
|
391
|
+
},
|
|
392
|
+
seg.group || ''
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
if (extraSpan > 0) {
|
|
396
|
+
cells.push(_react2.default.createElement('th', { key: 'hgSegExtra', colSpan: extraSpan, rowSpan: rowSpan }));
|
|
397
|
+
}
|
|
398
|
+
return cells;
|
|
399
|
+
};
|
|
400
|
+
// --- End row group header ---
|
|
401
|
+
|
|
402
|
+
var rowKeys = pivotData.getRowKeys();
|
|
403
|
+
var baseRowKeys = hasMultipleAggregators && !isColumnAggregationMode && rowKeys.length === 0 ? [[]] : rowKeys;
|
|
404
|
+
var renderedRows = hasMultipleAggregators && !isColumnAggregationMode ? baseRowKeys.reduce(function (acc, rowKey) {
|
|
405
|
+
aggregations.forEach(function (agg) {
|
|
406
|
+
acc.push({
|
|
407
|
+
displayKey: rowKey.concat([agg.label || agg.aggregatorName]),
|
|
408
|
+
actualKey: rowKey,
|
|
409
|
+
aggregationKey: agg.key
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
return acc;
|
|
413
|
+
}, []) : baseRowKeys.map(function (rowKey) {
|
|
414
|
+
return {
|
|
415
|
+
displayKey: rowKey,
|
|
416
|
+
actualKey: rowKey,
|
|
417
|
+
aggregationKey: primaryAggregation.key
|
|
418
|
+
};
|
|
419
|
+
});
|
|
420
|
+
var rowKeyValues = renderedRows.map(function (r) {
|
|
421
|
+
return r.displayKey;
|
|
422
|
+
});
|
|
423
|
+
var colKeys = pivotData.getColKeys();
|
|
424
|
+
var baseColKeys = isColumnAggregationMode && colKeys.length === 0 ? [[]] : colKeys;
|
|
425
|
+
var columnMeta = isColumnAggregationMode ? baseColKeys.reduce(function (acc, colKey) {
|
|
426
|
+
aggregations.forEach(function (agg) {
|
|
427
|
+
acc.push({
|
|
428
|
+
displayKey: colKey.concat([agg.label || agg.aggregatorName]),
|
|
429
|
+
actualKey: colKey,
|
|
430
|
+
aggregationKey: agg.key
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
return acc;
|
|
434
|
+
}, []) : colKeys.map(function (colKey) {
|
|
435
|
+
return {
|
|
436
|
+
displayKey: colKey,
|
|
437
|
+
actualKey: colKey,
|
|
438
|
+
aggregationKey: primaryAggregation.key
|
|
439
|
+
};
|
|
440
|
+
});
|
|
441
|
+
var colKeyValues = columnMeta.map(function (col) {
|
|
442
|
+
return col.displayKey;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
var renderAggregatorHeaderCells = function renderAggregatorHeaderCells() {
|
|
446
|
+
return columnMeta.map(function (colMeta, idx) {
|
|
447
|
+
var label = colMeta.displayKey[colMeta.displayKey.length - 1] || '';
|
|
448
|
+
// Calculate colspan/rowspan so aggregator headers line up with column groups
|
|
449
|
+
// aggregator index is the last element in displayKey (after column attrs)
|
|
450
|
+
var aggIndex = colAttrsToRender.length;
|
|
451
|
+
// When there are column attributes, each aggregator should appear once per column group,
|
|
452
|
+
// so each aggregator header should have colSpan = 1.
|
|
453
|
+
// When there are no column attributes, use spanSize to merge identical aggregator labels.
|
|
454
|
+
var colSpan = void 0;
|
|
455
|
+
if (hasColumnAttributes) {
|
|
456
|
+
// Each aggregator appears once per column group, so colSpan is always 1
|
|
457
|
+
// This ensures aggregator headers are listed in each column group and align
|
|
458
|
+
// correctly with the column groups above (which have colSpan = number of aggregators)
|
|
459
|
+
colSpan = 1;
|
|
460
|
+
} else {
|
|
461
|
+
// Use spanSize to merge identical aggregator labels when there are no column attributes
|
|
462
|
+
colSpan = spanSize(colKeyValues, idx, aggIndex);
|
|
463
|
+
// If spanSize returned -1 it means this cell is merged into a previous one
|
|
464
|
+
if (colSpan === -1) {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// When there are column attributes, rowSpan should account for the row header row
|
|
470
|
+
// When there are no column attributes, rowSpan is 1
|
|
471
|
+
var rowSpan = hasColumnAttributes && rowAttrsToRender.length !== 0 ? 1 : 1;
|
|
472
|
+
|
|
473
|
+
return _react2.default.createElement(
|
|
474
|
+
'th',
|
|
475
|
+
{
|
|
476
|
+
className: 'pvtColLabel',
|
|
477
|
+
key: 'agg-label-' + idx,
|
|
478
|
+
colSpan: colSpan,
|
|
479
|
+
rowSpan: rowSpan
|
|
480
|
+
},
|
|
481
|
+
label
|
|
482
|
+
);
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
var valueCellColors = function valueCellColors() {};
|
|
487
|
+
var rowTotalColors = function rowTotalColors() {};
|
|
488
|
+
var colTotalColors = function colTotalColors() {};
|
|
489
|
+
if (opts.heatmapMode && !hasMultipleAggregators) {
|
|
490
|
+
var colorScaleGenerator = this.props.tableColorScaleGenerator;
|
|
491
|
+
var rowTotalValues = colKeys.map(function (x) {
|
|
492
|
+
return pivotData.getAggregator([], x, primaryAggregation.key).value();
|
|
493
|
+
});
|
|
494
|
+
rowTotalColors = colorScaleGenerator(rowTotalValues);
|
|
495
|
+
var colTotalValues = rowKeys.map(function (x) {
|
|
496
|
+
return pivotData.getAggregator(x, [], primaryAggregation.key).value();
|
|
497
|
+
});
|
|
498
|
+
colTotalColors = colorScaleGenerator(colTotalValues);
|
|
499
|
+
|
|
500
|
+
if (opts.heatmapMode === 'full') {
|
|
501
|
+
var allValues = [];
|
|
502
|
+
rowKeys.map(function (r) {
|
|
503
|
+
return colKeys.map(function (c) {
|
|
504
|
+
return allValues.push(pivotData.getAggregator(r, c, primaryAggregation.key).value());
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
var colorScale = colorScaleGenerator(allValues);
|
|
508
|
+
valueCellColors = function valueCellColors(r, c, v) {
|
|
509
|
+
return colorScale(v);
|
|
510
|
+
};
|
|
511
|
+
} else if (opts.heatmapMode === 'row') {
|
|
512
|
+
var rowColorScales = {};
|
|
513
|
+
rowKeys.map(function (r) {
|
|
514
|
+
var rowValues = colKeys.map(function (x) {
|
|
515
|
+
return pivotData.getAggregator(r, x, primaryAggregation.key).value();
|
|
516
|
+
});
|
|
517
|
+
rowColorScales[r] = colorScaleGenerator(rowValues);
|
|
518
|
+
});
|
|
519
|
+
valueCellColors = function valueCellColors(r, c, v) {
|
|
520
|
+
return rowColorScales[r](v);
|
|
521
|
+
};
|
|
522
|
+
} else if (opts.heatmapMode === 'col') {
|
|
523
|
+
var colColorScales = {};
|
|
524
|
+
colKeys.map(function (c) {
|
|
525
|
+
var colValues = rowKeys.map(function (x) {
|
|
526
|
+
return pivotData.getAggregator(x, c, primaryAggregation.key).value();
|
|
527
|
+
});
|
|
528
|
+
colColorScales[c] = colorScaleGenerator(colValues);
|
|
529
|
+
});
|
|
530
|
+
valueCellColors = function valueCellColors(r, c, v) {
|
|
531
|
+
return colColorScales[c](v);
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Get conditional formatting from tableOptions
|
|
537
|
+
var conditionalFormatting = tableOptions.conditionalFormatting || null;
|
|
538
|
+
|
|
539
|
+
// Get cell formatting from tableOptions
|
|
540
|
+
var cellFormatting = tableOptions.cellFormatting || null;
|
|
541
|
+
|
|
542
|
+
// Create lookup map from aggregationKey to aggregatorName
|
|
543
|
+
var aggregationNameLookup = {};
|
|
544
|
+
aggregations.forEach(function (agg) {
|
|
545
|
+
aggregationNameLookup[agg.key] = agg.aggregatorName;
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// Helper function to get cell style with conditional formatting
|
|
549
|
+
var getCellStyle = function getCellStyle(value) {
|
|
550
|
+
var heatmapStyle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
551
|
+
|
|
552
|
+
var conditionalStyle = getConditionalFormattingStyle(value, conditionalFormatting);
|
|
553
|
+
return mergeStyles(heatmapStyle, conditionalStyle);
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
// Helper function to format cell value with cell formatting rules
|
|
557
|
+
var formatCellValue = function formatCellValue(value, aggregator, aggregationKey) {
|
|
558
|
+
var aggregatorName = aggregationKey ? aggregationNameLookup[aggregationKey] : null;
|
|
559
|
+
|
|
560
|
+
return applyCellFormatting(value, cellFormatting, aggregator.format.bind(aggregator), aggregatorName);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
// Helper function to format label text if it's numeric
|
|
564
|
+
// Used for both row and column labels
|
|
565
|
+
var formatLabel = function formatLabel(txt) {
|
|
566
|
+
// Check if txt is numeric (number or numeric string)
|
|
567
|
+
// Also exclude 'null' string which is used for missing values
|
|
568
|
+
if (txt === 'null' || txt === null || typeof txt === 'undefined') {
|
|
569
|
+
return txt;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Don't format if it contains special characters (dates, formatted strings, etc.)
|
|
573
|
+
if (containsSpecialCharacters(txt)) {
|
|
574
|
+
return txt;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Check if txt is numeric (number or numeric string)
|
|
578
|
+
// A string is numeric if it can be parsed as a finite number
|
|
579
|
+
// Exclude strings with special characters from formatting
|
|
580
|
+
var isNumeric = false;
|
|
581
|
+
var numericValue = null;
|
|
582
|
+
|
|
583
|
+
if (typeof txt === 'number') {
|
|
584
|
+
isNumeric = !isNaN(txt) && isFinite(txt);
|
|
585
|
+
if (isNumeric) {
|
|
586
|
+
numericValue = txt;
|
|
587
|
+
}
|
|
588
|
+
} else if (typeof txt === 'string' && txt.trim() !== '' && txt !== 'null' && !containsSpecialCharacters(txt)) {
|
|
589
|
+
var parsed = parseFloat(txt.trim());
|
|
590
|
+
isNumeric = !isNaN(parsed) && isFinite(parsed);
|
|
591
|
+
if (isNumeric) {
|
|
592
|
+
numericValue = parsed;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (isNumeric && numericValue !== null && cellFormatting && Array.isArray(cellFormatting.rules) && cellFormatting.rules.length > 0) {
|
|
597
|
+
var matchingRule = cellFormatting.rules[0];
|
|
598
|
+
if (matchingRule && matchingRule.format) {
|
|
599
|
+
var format = matchingRule.format;
|
|
600
|
+
var formatter = (0, _Utilities.numberFormat)({
|
|
601
|
+
digitsAfterDecimal: 'decimalPlaces' in format ? format.decimalPlaces : 2,
|
|
602
|
+
thousandsSep: 'thousandsSep' in format ? format.thousandsSep : ',',
|
|
603
|
+
decimalSep: 'decimalSep' in format ? format.decimalSep : '.',
|
|
604
|
+
prefix: 'prefix' in format ? format.prefix : '',
|
|
605
|
+
suffix: 'suffix' in format ? format.suffix : '',
|
|
606
|
+
showOriginal: 'showOriginal' in format ? format.showOriginal : false
|
|
607
|
+
});
|
|
608
|
+
return formatter(numericValue);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Not numeric or no formatting rules, return as-is
|
|
613
|
+
return txt;
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// Helper function to format row label text if it's numeric
|
|
617
|
+
// Only formats if it's a numeric value, not aggregation labels
|
|
618
|
+
var formatRowLabel = function formatRowLabel(txt, index, isLastElement, hasMultipleAggregators, rowAttrsLength) {
|
|
619
|
+
// Don't format the last element if it's an aggregation label (when multiple aggregators)
|
|
620
|
+
// The last element is an aggregation label only if:
|
|
621
|
+
// 1. There are multiple aggregators
|
|
622
|
+
// 2. AND it's beyond the row attributes (index >= rowAttrsLength)
|
|
623
|
+
if (hasMultipleAggregators && isLastElement && index >= rowAttrsLength) {
|
|
624
|
+
return txt;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return formatLabel(txt);
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
var getClickHandler = tableOptions && tableOptions.clickCallback ? function (value, rowValues, colValues) {
|
|
631
|
+
var filters = {};
|
|
632
|
+
var _iteratorNormalCompletion2 = true;
|
|
633
|
+
var _didIteratorError2 = false;
|
|
634
|
+
var _iteratorError2 = undefined;
|
|
635
|
+
|
|
636
|
+
try {
|
|
637
|
+
for (var _iterator2 = Object.keys(colAttrs || {})[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
638
|
+
var i = _step2.value;
|
|
639
|
+
|
|
640
|
+
var attr = colAttrs[i];
|
|
641
|
+
if (colValues[i] !== null) {
|
|
642
|
+
filters[attr] = colValues[i];
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
} catch (err) {
|
|
646
|
+
_didIteratorError2 = true;
|
|
647
|
+
_iteratorError2 = err;
|
|
648
|
+
} finally {
|
|
649
|
+
try {
|
|
650
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
651
|
+
_iterator2.return();
|
|
652
|
+
}
|
|
653
|
+
} finally {
|
|
654
|
+
if (_didIteratorError2) {
|
|
655
|
+
throw _iteratorError2;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
var _iteratorNormalCompletion3 = true;
|
|
661
|
+
var _didIteratorError3 = false;
|
|
662
|
+
var _iteratorError3 = undefined;
|
|
663
|
+
|
|
664
|
+
try {
|
|
665
|
+
for (var _iterator3 = Object.keys(rowAttrs || {})[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
666
|
+
var _i = _step3.value;
|
|
667
|
+
|
|
668
|
+
var attr = rowAttrs[_i];
|
|
669
|
+
if (rowValues[_i] !== null) {
|
|
670
|
+
filters[attr] = rowValues[_i];
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
} catch (err) {
|
|
674
|
+
_didIteratorError3 = true;
|
|
675
|
+
_iteratorError3 = err;
|
|
676
|
+
} finally {
|
|
677
|
+
try {
|
|
678
|
+
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
679
|
+
_iterator3.return();
|
|
680
|
+
}
|
|
681
|
+
} finally {
|
|
682
|
+
if (_didIteratorError3) {
|
|
683
|
+
throw _iteratorError3;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return function (e) {
|
|
689
|
+
return tableOptions.clickCallback(e, value, filters, pivotData);
|
|
690
|
+
};
|
|
691
|
+
} : null;
|
|
692
|
+
|
|
693
|
+
var handleColSort = function handleColSort(attr, e) {
|
|
694
|
+
e.stopPropagation();
|
|
695
|
+
if (_this2.props.onColSort) {
|
|
696
|
+
var currentSort = _this2.props.colSorts && _this2.props.colSorts[attr];
|
|
697
|
+
var nextSort = 'ASC';
|
|
698
|
+
if (currentSort === 'ASC') {
|
|
699
|
+
nextSort = 'DESC';
|
|
700
|
+
} else if (currentSort === 'DESC') {
|
|
701
|
+
nextSort = null;
|
|
702
|
+
}
|
|
703
|
+
_this2.props.onColSort(attr, nextSort);
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
var handleRowSort = function handleRowSort(attr, e) {
|
|
708
|
+
e.stopPropagation();
|
|
709
|
+
if (_this2.props.onRowSort) {
|
|
710
|
+
var currentSort = _this2.props.rowSorts && _this2.props.rowSorts[attr];
|
|
711
|
+
var nextSort = 'ASC';
|
|
712
|
+
if (currentSort === 'ASC') {
|
|
713
|
+
nextSort = 'DESC';
|
|
714
|
+
} else if (currentSort === 'DESC') {
|
|
715
|
+
nextSort = null;
|
|
716
|
+
}
|
|
717
|
+
_this2.props.onRowSort(attr, nextSort);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
var getSortButton = function getSortButton(attr, isCol) {
|
|
722
|
+
var currentSort = isCol ? _this2.props.colSorts && _this2.props.colSorts[attr] : _this2.props.rowSorts && _this2.props.rowSorts[attr];
|
|
723
|
+
var handleSort = isCol ? handleColSort : handleRowSort;
|
|
724
|
+
var sortIcon = '';
|
|
725
|
+
if (currentSort !== null) {
|
|
726
|
+
if (isCol) {
|
|
727
|
+
sortIcon = _this2.props.colSortIcons[currentSort] || _this2.props.colSortIcons.DEFAULT;
|
|
728
|
+
} else {
|
|
729
|
+
sortIcon = _this2.props.rowSortIcons[currentSort] || _this2.props.rowSortIcons.DEFAULT;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return _react2.default.createElement(
|
|
733
|
+
'button',
|
|
734
|
+
{
|
|
735
|
+
type: 'button',
|
|
736
|
+
className: 'pvtSortButton',
|
|
737
|
+
onClick: function onClick(e) {
|
|
738
|
+
return handleSort(attr, e);
|
|
739
|
+
},
|
|
740
|
+
title: currentSort === 'ASC' ? 'Sort Descending' : currentSort === 'DESC' ? 'Clear Sort' : 'Sort Ascending'
|
|
741
|
+
},
|
|
742
|
+
sortIcon
|
|
743
|
+
);
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
var totalRowAggregations = isColumnAggregationMode ? [primaryAggregation] : hasMultipleAggregators ? aggregations : [primaryAggregation];
|
|
747
|
+
var totalsLabelText = 'Totals';
|
|
748
|
+
|
|
749
|
+
return _react2.default.createElement(
|
|
750
|
+
'table',
|
|
751
|
+
{ className: 'pvtTable' },
|
|
752
|
+
_react2.default.createElement(
|
|
753
|
+
'thead',
|
|
754
|
+
null,
|
|
755
|
+
colAttrsToRender.map(function (c, j) {
|
|
756
|
+
|
|
757
|
+
return _react2.default.createElement(
|
|
758
|
+
'tr',
|
|
759
|
+
{ key: 'colAttr' + j },
|
|
760
|
+
shouldRenderRowHeaderPlaceholder && (isColumnAggregationMode || j === 0) && !(isColumnAggregationMode && hasColumnAttributes && rowAttrsToRender.length === 1) && (hasHeaderGroup && j === 0 && !isColumnAggregationMode ? renderGroupSegments(colAttrsToRender.length, headerGroupExtraSpan) : _react2.default.createElement('th', {
|
|
761
|
+
colSpan: isColumnAggregationMode && hasColumnAttributes ? rowAttrsToRender.length - 1 : rowAttrsToRender.length,
|
|
762
|
+
rowSpan: isColumnAggregationMode && hasColumnAttributes ? 1 : colAttrsToRender.length
|
|
763
|
+
})),
|
|
764
|
+
c ? _react2.default.createElement(
|
|
765
|
+
'th',
|
|
766
|
+
{ className: 'pvtAxisLabel' },
|
|
767
|
+
c,
|
|
768
|
+
colAttrs.includes(c) && getSortButton(c, true)
|
|
769
|
+
) : null,
|
|
770
|
+
colKeyValues.map(function (colKey, i) {
|
|
771
|
+
var x = spanSize(colKeyValues, i, j);
|
|
772
|
+
|
|
773
|
+
if (x === -1) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
var formattedColLabel = formatLabel(colKey[j]);
|
|
778
|
+
return _react2.default.createElement(
|
|
779
|
+
'th',
|
|
780
|
+
{
|
|
781
|
+
className: 'pvtColLabel',
|
|
782
|
+
key: 'colKey' + i,
|
|
783
|
+
colSpan: x,
|
|
784
|
+
rowSpan: j === colAttrsToRender.length - 1 && rowAttrsToRender.length !== 0 && !isColumnAggregationMode ? 2 : 1
|
|
785
|
+
},
|
|
786
|
+
formattedColLabel
|
|
787
|
+
);
|
|
788
|
+
}),
|
|
789
|
+
showTotalsColumn && j === 0 && _react2.default.createElement(
|
|
790
|
+
'th',
|
|
791
|
+
{
|
|
792
|
+
className: 'pvtTotalLabel',
|
|
793
|
+
rowSpan: colAttrsToRender.length + (rowAttrsToRender.length === 0 ? 0 : 1)
|
|
794
|
+
},
|
|
795
|
+
totalsLabelText
|
|
796
|
+
)
|
|
797
|
+
);
|
|
798
|
+
}),
|
|
799
|
+
hasHeaderGroup && colAttrsToRender.length === 0 && rowAttrsToRender.length !== 0 && _react2.default.createElement(
|
|
800
|
+
'tr',
|
|
801
|
+
null,
|
|
802
|
+
renderGroupSegments(1, headerGroupExtraSpan),
|
|
803
|
+
_react2.default.createElement('th', null)
|
|
804
|
+
),
|
|
805
|
+
isColumnAggregationMode && hasColumnAttributes && _react2.default.createElement(
|
|
806
|
+
'tr',
|
|
807
|
+
null,
|
|
808
|
+
rowAttrsToRender.length !== 0 && rowAttrsToRender.map(function (r, i) {
|
|
809
|
+
var isRowAttr = i < rowAttrs.length;
|
|
810
|
+
var attr = isRowAttr ? rowAttrs[i] : null;
|
|
811
|
+
return _react2.default.createElement(
|
|
812
|
+
'th',
|
|
813
|
+
{ className: 'pvtAxisLabel', key: 'rowAttr-agg-' + i },
|
|
814
|
+
r,
|
|
815
|
+
isRowAttr && attr && getSortButton(attr, false)
|
|
816
|
+
);
|
|
817
|
+
}),
|
|
818
|
+
renderAggregatorHeaderCells()
|
|
819
|
+
),
|
|
820
|
+
rowAttrsToRender.length !== 0 && !(isColumnAggregationMode && hasColumnAttributes) && _react2.default.createElement(
|
|
821
|
+
'tr',
|
|
822
|
+
null,
|
|
823
|
+
rowAttrsToRender.map(function (r, i) {
|
|
824
|
+
var isRowAttr = i < rowAttrs.length;
|
|
825
|
+
var attr = isRowAttr ? rowAttrs[i] : null;
|
|
826
|
+
return _react2.default.createElement(
|
|
827
|
+
'th',
|
|
828
|
+
{ className: 'pvtAxisLabel', key: 'rowAttr' + i },
|
|
829
|
+
r,
|
|
830
|
+
isRowAttr && attr && getSortButton(attr, false)
|
|
831
|
+
);
|
|
832
|
+
}),
|
|
833
|
+
isColumnAggregationMode && !hasColumnAttributes ? renderAggregatorHeaderCells() : null,
|
|
834
|
+
showTotalsColumn && _react2.default.createElement(
|
|
835
|
+
'th',
|
|
836
|
+
{ className: 'pvtTotalLabel' },
|
|
837
|
+
colAttrs.length === 0 ? totalsLabelText : null
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
),
|
|
841
|
+
_react2.default.createElement(
|
|
842
|
+
'tbody',
|
|
843
|
+
null,
|
|
844
|
+
renderedRows.map(function (rowMeta, i) {
|
|
845
|
+
var totalAggregator = pivotData.getAggregator(rowMeta.actualKey, [], rowMeta.aggregationKey);
|
|
846
|
+
var displayKey = rowMeta.displayKey;
|
|
847
|
+
return _react2.default.createElement(
|
|
848
|
+
'tr',
|
|
849
|
+
{ key: 'rowKeyRow' + i },
|
|
850
|
+
displayKey.map(function (txt, j) {
|
|
851
|
+
var x = spanSize(rowKeyValues, i, j);
|
|
852
|
+
if (x === -1) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
var isLastElement = j === displayKey.length - 1;
|
|
856
|
+
var formattedTxt = formatRowLabel(txt, j, isLastElement, hasMultipleAggregators, rowAttrs.length);
|
|
857
|
+
return _react2.default.createElement(
|
|
858
|
+
'th',
|
|
859
|
+
{
|
|
860
|
+
key: 'rowKeyLabel' + i + '-' + j,
|
|
861
|
+
className: 'pvtRowLabel',
|
|
862
|
+
rowSpan: x,
|
|
863
|
+
colSpan: showTotalsColumn && j === rowAttrsToRender.length - 1 && colAttrsToRender.length !== 0 ? 2 : 1
|
|
864
|
+
},
|
|
865
|
+
formattedTxt
|
|
866
|
+
);
|
|
867
|
+
}),
|
|
868
|
+
columnMeta.map(function (colMeta, j) {
|
|
869
|
+
var aggregationKeyForCell = isColumnAggregationMode ? colMeta.aggregationKey : rowMeta.aggregationKey;
|
|
870
|
+
var aggregator = pivotData.getAggregator(rowMeta.actualKey, colMeta.actualKey, aggregationKeyForCell);
|
|
871
|
+
var value = aggregator.value();
|
|
872
|
+
var heatmapStyle = valueCellColors(rowMeta.actualKey, colMeta.actualKey, value) || {};
|
|
873
|
+
return _react2.default.createElement(
|
|
874
|
+
'td',
|
|
875
|
+
{
|
|
876
|
+
className: 'pvtVal',
|
|
877
|
+
key: 'pvtVal' + i + '-' + j,
|
|
878
|
+
onClick: getClickHandler && getClickHandler(value, rowMeta.actualKey, colMeta.actualKey),
|
|
879
|
+
style: getCellStyle(value, heatmapStyle)
|
|
880
|
+
},
|
|
881
|
+
formatCellValue(value, aggregator, aggregationKeyForCell)
|
|
882
|
+
);
|
|
883
|
+
}),
|
|
884
|
+
showTotalsColumn && _react2.default.createElement(
|
|
885
|
+
'td',
|
|
886
|
+
{
|
|
887
|
+
className: 'pvtTotal',
|
|
888
|
+
onClick: getClickHandler && getClickHandler(totalAggregator.value(), rowMeta.actualKey, [null]),
|
|
889
|
+
style: getCellStyle(totalAggregator.value(), colTotalColors(totalAggregator.value()) || {})
|
|
890
|
+
},
|
|
891
|
+
formatCellValue(totalAggregator.value(), totalAggregator, rowMeta.aggregationKey)
|
|
892
|
+
)
|
|
893
|
+
);
|
|
894
|
+
}),
|
|
895
|
+
totalRowAggregations.map(function (agg) {
|
|
896
|
+
var grandTotalAggregator = pivotData.getAggregator([], [], isColumnAggregationMode ? primaryAggregation.key : agg.key);
|
|
897
|
+
var totalLabel = hasMultipleAggregators && !isColumnAggregationMode ? 'Totals \u2013 ' + (agg.label || agg.aggregatorName) : totalsLabelText;
|
|
898
|
+
return _react2.default.createElement(
|
|
899
|
+
'tr',
|
|
900
|
+
{ key: 'totalRow-' + agg.key },
|
|
901
|
+
_react2.default.createElement(
|
|
902
|
+
'th',
|
|
903
|
+
{
|
|
904
|
+
className: 'pvtTotalLabel',
|
|
905
|
+
colSpan: rowAttrsToRender.length + (colAttrs.length === 0 || isColumnAggregationMode && hasColumnAttributes ? 0 : 1)
|
|
906
|
+
},
|
|
907
|
+
totalLabel
|
|
908
|
+
),
|
|
909
|
+
columnMeta.map(function (colMeta, i) {
|
|
910
|
+
var totalAggregator = pivotData.getAggregator([], colMeta.actualKey, isColumnAggregationMode ? colMeta.aggregationKey : agg.key);
|
|
911
|
+
var totalValue = totalAggregator.value();
|
|
912
|
+
return _react2.default.createElement(
|
|
913
|
+
'td',
|
|
914
|
+
{
|
|
915
|
+
className: 'pvtTotal',
|
|
916
|
+
key: 'total' + agg.key + '-' + i,
|
|
917
|
+
onClick: getClickHandler && getClickHandler(totalValue, [null], colMeta.actualKey),
|
|
918
|
+
style: getCellStyle(totalValue, rowTotalColors(totalValue) || {})
|
|
919
|
+
},
|
|
920
|
+
formatCellValue(totalValue, totalAggregator, agg.key)
|
|
921
|
+
);
|
|
922
|
+
}),
|
|
923
|
+
showTotalsColumn && _react2.default.createElement(
|
|
924
|
+
'td',
|
|
925
|
+
{
|
|
926
|
+
onClick: getClickHandler && getClickHandler(grandTotalAggregator.value(), [null], [null]),
|
|
927
|
+
className: 'pvtGrandTotal',
|
|
928
|
+
style: getCellStyle(grandTotalAggregator.value(), {})
|
|
929
|
+
},
|
|
930
|
+
formatCellValue(grandTotalAggregator.value(), grandTotalAggregator, isColumnAggregationMode ? primaryAggregation.key : agg.key)
|
|
931
|
+
)
|
|
932
|
+
);
|
|
933
|
+
})
|
|
934
|
+
)
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
}]);
|
|
938
|
+
|
|
939
|
+
return TableRenderer;
|
|
940
|
+
}(_react2.default.PureComponent);
|
|
941
|
+
|
|
942
|
+
TableRenderer.defaultProps = _Utilities.PivotData.defaultProps;
|
|
943
|
+
TableRenderer.propTypes = _Utilities.PivotData.propTypes;
|
|
944
|
+
TableRenderer.defaultProps.tableColorScaleGenerator = redColorScaleGenerator;
|
|
945
|
+
TableRenderer.defaultProps.tableOptions = {};
|
|
946
|
+
TableRenderer.defaultProps.colSorts = {};
|
|
947
|
+
TableRenderer.defaultProps.rowSorts = {};
|
|
948
|
+
TableRenderer.defaultProps.rowGroupConfig = null;
|
|
949
|
+
TableRenderer.propTypes.tableColorScaleGenerator = _propTypes2.default.func;
|
|
950
|
+
TableRenderer.propTypes.tableOptions = _propTypes2.default.object;
|
|
951
|
+
TableRenderer.propTypes.colSorts = _propTypes2.default.objectOf(_propTypes2.default.oneOf(['ASC', 'DESC']));
|
|
952
|
+
TableRenderer.propTypes.rowSorts = _propTypes2.default.objectOf(_propTypes2.default.oneOf(['ASC', 'DESC']));
|
|
953
|
+
TableRenderer.propTypes.onColSort = _propTypes2.default.func;
|
|
954
|
+
TableRenderer.propTypes.onRowSort = _propTypes2.default.func;
|
|
955
|
+
TableRenderer.propTypes.rowGroupConfig = _propTypes2.default.arrayOf(_propTypes2.default.shape({
|
|
956
|
+
label: _propTypes2.default.string.isRequired,
|
|
957
|
+
match: _propTypes2.default.arrayOf(_propTypes2.default.string).isRequired
|
|
958
|
+
}));
|
|
959
|
+
TableRenderer.defaultProps.colSortIcons = {
|
|
960
|
+
ASC: '→',
|
|
961
|
+
DESC: '←',
|
|
962
|
+
DEFAULT: '↔'
|
|
963
|
+
};
|
|
964
|
+
TableRenderer.defaultProps.rowSortIcons = {
|
|
965
|
+
ASC: '↑',
|
|
966
|
+
DESC: '↓',
|
|
967
|
+
DEFAULT: '⇅'
|
|
968
|
+
};
|
|
969
|
+
TableRenderer.propTypes.colSortIcons = _propTypes2.default.shape({
|
|
970
|
+
ASC: _propTypes2.default.node,
|
|
971
|
+
DESC: _propTypes2.default.node,
|
|
972
|
+
DEFAULT: _propTypes2.default.node
|
|
973
|
+
});
|
|
974
|
+
TableRenderer.propTypes.rowSortIcons = _propTypes2.default.shape({
|
|
975
|
+
ASC: _propTypes2.default.node,
|
|
976
|
+
DESC: _propTypes2.default.node,
|
|
977
|
+
DEFAULT: _propTypes2.default.node
|
|
978
|
+
});
|
|
979
|
+
return TableRenderer;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
var TSVExportRenderer = function (_React$PureComponent2) {
|
|
983
|
+
_inherits(TSVExportRenderer, _React$PureComponent2);
|
|
984
|
+
|
|
985
|
+
function TSVExportRenderer() {
|
|
986
|
+
_classCallCheck(this, TSVExportRenderer);
|
|
987
|
+
|
|
988
|
+
return _possibleConstructorReturn(this, (TSVExportRenderer.__proto__ || Object.getPrototypeOf(TSVExportRenderer)).apply(this, arguments));
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
_createClass(TSVExportRenderer, [{
|
|
992
|
+
key: 'render',
|
|
993
|
+
value: function render() {
|
|
994
|
+
var pivotData = new _Utilities.PivotData(this.props);
|
|
995
|
+
var rowAttrs = pivotData.props.rows;
|
|
996
|
+
var aggregations = pivotData.getAggregations();
|
|
997
|
+
var hasMultipleAggregators = aggregations.length > 1;
|
|
998
|
+
var rowKeys = pivotData.getRowKeys();
|
|
999
|
+
var colKeys = pivotData.getColKeys();
|
|
1000
|
+
var safeRowKeys = rowKeys.length === 0 ? [[]] : rowKeys;
|
|
1001
|
+
var safeColKeys = colKeys.length === 0 ? [[]] : colKeys;
|
|
1002
|
+
|
|
1003
|
+
var rowEntries = hasMultipleAggregators ? safeRowKeys.reduce(function (acc, rowKey) {
|
|
1004
|
+
aggregations.forEach(function (agg) {
|
|
1005
|
+
acc.push({ rowKey: rowKey, aggregationKey: agg.key, label: agg.label });
|
|
1006
|
+
});
|
|
1007
|
+
return acc;
|
|
1008
|
+
}, []) : safeRowKeys.map(function (rowKey) {
|
|
1009
|
+
return {
|
|
1010
|
+
rowKey: rowKey,
|
|
1011
|
+
aggregationKey: aggregations[0].key,
|
|
1012
|
+
label: aggregations[0].label || aggregations[0].aggregatorName
|
|
1013
|
+
};
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
var headerRow = rowAttrs.slice();
|
|
1017
|
+
if (hasMultipleAggregators) {
|
|
1018
|
+
headerRow.push('Values');
|
|
1019
|
+
}
|
|
1020
|
+
if (safeColKeys.length === 1 && safeColKeys[0].length === 0) {
|
|
1021
|
+
headerRow.push(hasMultipleAggregators ? 'Value' : aggregations[0].label || aggregations[0].aggregatorName);
|
|
1022
|
+
} else {
|
|
1023
|
+
safeColKeys.map(function (c) {
|
|
1024
|
+
return headerRow.push(c.join('-'));
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
var result = rowEntries.map(function (entry) {
|
|
1029
|
+
var row = entry.rowKey.map(function (x) {
|
|
1030
|
+
return x;
|
|
1031
|
+
});
|
|
1032
|
+
if (hasMultipleAggregators) {
|
|
1033
|
+
row.push(entry.label || '');
|
|
1034
|
+
}
|
|
1035
|
+
safeColKeys.map(function (colKey) {
|
|
1036
|
+
var v = pivotData.getAggregator(entry.rowKey, colKey, entry.aggregationKey).value();
|
|
1037
|
+
row.push(v ? v : '');
|
|
1038
|
+
});
|
|
1039
|
+
return row;
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
result.unshift(headerRow);
|
|
1043
|
+
|
|
1044
|
+
return _react2.default.createElement('textarea', {
|
|
1045
|
+
value: result.map(function (r) {
|
|
1046
|
+
return r.join('\t');
|
|
1047
|
+
}).join('\n'),
|
|
1048
|
+
style: { width: window.innerWidth / 2, height: window.innerHeight / 2 },
|
|
1049
|
+
readOnly: true
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}]);
|
|
1053
|
+
|
|
1054
|
+
return TSVExportRenderer;
|
|
1055
|
+
}(_react2.default.PureComponent);
|
|
1056
|
+
|
|
1057
|
+
TSVExportRenderer.defaultProps = _Utilities.PivotData.defaultProps;
|
|
1058
|
+
TSVExportRenderer.propTypes = _Utilities.PivotData.propTypes;
|
|
1059
|
+
|
|
1060
|
+
exports.default = {
|
|
1061
|
+
Table: makeRenderer(),
|
|
1062
|
+
'Table Heatmap': makeRenderer({ heatmapMode: 'full' }),
|
|
1063
|
+
'Table Col Heatmap': makeRenderer({ heatmapMode: 'col' }),
|
|
1064
|
+
'Table Row Heatmap': makeRenderer({ heatmapMode: 'row' }),
|
|
1065
|
+
'Exportable TSV': TSVExportRenderer
|
|
1066
|
+
};
|
|
1067
|
+
//# sourceMappingURL=TableRenderers.js.map
|