@hrforte/react-pivottable 1.0.1 → 2.0.0

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.
@@ -3,11 +3,18 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.CellFormattingUI = exports.ConditionalFormattingUI = exports.Dropdown = exports.DraggableAttribute = undefined;
7
6
 
8
7
  var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
9
8
 
10
- 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; }; }();
9
+ var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /* eslint-disable no-magic-numbers */
10
+ /* eslint-disable no-undefined */
11
+
12
+
13
+ exports.DraggableAttribute = DraggableAttribute;
14
+ exports.Dropdown = Dropdown;
15
+ exports.ConditionalFormattingUI = ConditionalFormattingUI;
16
+ exports.CellFormattingUI = CellFormattingUI;
17
+ exports.PivotLoadingOverlay = PivotLoadingOverlay;
11
18
 
12
19
  var _react = require('react');
13
20
 
@@ -23,6 +30,10 @@ var _immutabilityHelper2 = _interopRequireDefault(_immutabilityHelper);
23
30
 
24
31
  var _Utilities = require('./Utilities');
25
32
 
33
+ var _pivotCompute = require('./pivotCompute');
34
+
35
+ var _PivotDataWorker = require('./PivotDataWorker');
36
+
26
37
  var _PivotTable = require('./PivotTable');
27
38
 
28
39
  var _PivotTable2 = _interopRequireDefault(_PivotTable);
@@ -39,12 +50,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
39
50
 
40
51
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
41
52
 
42
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
43
-
44
- 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; }
45
-
46
- 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; }
47
-
48
53
  var AGG_KEY_RADIX = 36;
49
54
  var AGG_KEY_OFFSET = 2;
50
55
  var AGG_KEY_LENGTH = 7;
@@ -52,199 +57,190 @@ var AGG_KEY_LENGTH = 7;
52
57
  /* eslint-disable react/prop-types */
53
58
  // eslint can't see inherited propTypes!
54
59
 
55
- var DraggableAttribute = exports.DraggableAttribute = function (_React$Component) {
56
- _inherits(DraggableAttribute, _React$Component);
57
-
58
- function DraggableAttribute(props) {
59
- _classCallCheck(this, DraggableAttribute);
60
-
61
- var _this = _possibleConstructorReturn(this, (DraggableAttribute.__proto__ || Object.getPrototypeOf(DraggableAttribute)).call(this, props));
62
-
63
- _this.state = { open: false, filterText: '' };
64
- return _this;
65
- }
66
-
67
- _createClass(DraggableAttribute, [{
68
- key: 'toggleValue',
69
- value: function toggleValue(value) {
70
- if (value in this.props.valueFilter) {
71
- this.props.removeValuesFromFilter(this.props.name, [value]);
72
- } else {
73
- this.props.addValuesToFilter(this.props.name, [value]);
74
- }
75
- }
76
- }, {
77
- key: 'matchesFilter',
78
- value: function matchesFilter(x) {
79
- return x.toLowerCase().trim().includes(this.state.filterText.toLowerCase().trim());
80
- }
81
- }, {
82
- key: 'selectOnly',
83
- value: function selectOnly(e, value) {
84
- e.stopPropagation();
85
- this.props.setValuesInFilter(this.props.name, Object.keys(this.props.attrValues).filter(function (y) {
86
- return y !== value;
87
- }));
60
+ function DraggableAttribute(props) {
61
+ var name = props.name,
62
+ addValuesToFilter = props.addValuesToFilter,
63
+ removeValuesFromFilter = props.removeValuesFromFilter,
64
+ attrValues = props.attrValues,
65
+ valueFilter = props.valueFilter,
66
+ moveFilterBoxToTop = props.moveFilterBoxToTop,
67
+ sorter = props.sorter,
68
+ menuLimit = props.menuLimit,
69
+ zIndex = props.zIndex;
70
+
71
+ var _useState = (0, _react.useState)(false),
72
+ _useState2 = _slicedToArray(_useState, 2),
73
+ open = _useState2[0],
74
+ setOpen = _useState2[1];
75
+
76
+ var _useState3 = (0, _react.useState)(''),
77
+ _useState4 = _slicedToArray(_useState3, 2),
78
+ filterText = _useState4[0],
79
+ setFilterText = _useState4[1];
80
+
81
+ var toggleValue = function toggleValue(value) {
82
+ if (value in valueFilter) {
83
+ removeValuesFromFilter(name, [value]);
84
+ } else {
85
+ addValuesToFilter(name, [value]);
88
86
  }
89
- }, {
90
- key: 'getFilterBox',
91
- value: function getFilterBox() {
92
- var _this2 = this;
93
-
94
- var showMenu = Object.keys(this.props.attrValues).length < this.props.menuLimit;
95
-
96
- var values = Object.keys(this.props.attrValues);
97
- var shown = values.filter(this.matchesFilter.bind(this)).sort(this.props.sorter);
98
-
99
- return _react2.default.createElement(
100
- _reactDraggable2.default,
101
- { handle: '.pvtDragHandle' },
87
+ };
88
+
89
+ var matchesFilter = function matchesFilter(x) {
90
+ return x.toLowerCase().trim().includes(filterText.toLowerCase().trim());
91
+ };
92
+
93
+ var selectOnly = function selectOnly(e, value) {
94
+ e.stopPropagation();
95
+ props.setValuesInFilter(name, Object.keys(attrValues).filter(function (y) {
96
+ return y !== value;
97
+ }));
98
+ };
99
+
100
+ var toggleFilterBox = function toggleFilterBox() {
101
+ setOpen(!open);
102
+ moveFilterBoxToTop(name);
103
+ };
104
+
105
+ var getFilterBox = function getFilterBox() {
106
+ var showMenu = Object.keys(attrValues).length < menuLimit;
107
+
108
+ var values = Object.keys(attrValues);
109
+ var shown = values.filter(matchesFilter).sort(sorter);
110
+
111
+ return _react2.default.createElement(
112
+ _reactDraggable2.default,
113
+ { handle: '.pvtDragHandle' },
114
+ _react2.default.createElement(
115
+ 'div',
116
+ {
117
+ className: 'pvtFilterBox',
118
+ style: {
119
+ display: 'block',
120
+ cursor: 'initial',
121
+ zIndex: zIndex
122
+ },
123
+ onClick: function onClick() {
124
+ return moveFilterBoxToTop(name);
125
+ }
126
+ },
102
127
  _react2.default.createElement(
103
- 'div',
104
- {
105
- className: 'pvtFilterBox',
106
- style: {
107
- display: 'block',
108
- cursor: 'initial',
109
- zIndex: this.props.zIndex
110
- },
111
- onClick: function onClick() {
112
- return _this2.props.moveFilterBoxToTop(_this2.props.name);
128
+ 'a',
129
+ { onClick: function onClick() {
130
+ return setOpen(false);
131
+ }, className: 'pvtCloseX' },
132
+ '\xD7'
133
+ ),
134
+ _react2.default.createElement(
135
+ 'span',
136
+ { className: 'pvtDragHandle' },
137
+ '\u2630'
138
+ ),
139
+ _react2.default.createElement(
140
+ 'h4',
141
+ null,
142
+ name
143
+ ),
144
+ showMenu || _react2.default.createElement(
145
+ 'p',
146
+ null,
147
+ '(too many values to show)'
148
+ ),
149
+ showMenu && _react2.default.createElement(
150
+ 'p',
151
+ null,
152
+ _react2.default.createElement('input', {
153
+ type: 'text',
154
+ placeholder: 'Filter values',
155
+ className: 'pvtSearch',
156
+ value: filterText,
157
+ onChange: function onChange(e) {
158
+ return setFilterText(e.target.value);
113
159
  }
114
- },
160
+ }),
161
+ _react2.default.createElement('br', null),
115
162
  _react2.default.createElement(
116
163
  'a',
117
- { onClick: function onClick() {
118
- return _this2.setState({ open: false });
119
- }, className: 'pvtCloseX' },
120
- '\xD7'
121
- ),
122
- _react2.default.createElement(
123
- 'span',
124
- { className: 'pvtDragHandle' },
125
- '\u2630'
126
- ),
127
- _react2.default.createElement(
128
- 'h4',
129
- null,
130
- this.props.name
131
- ),
132
- showMenu || _react2.default.createElement(
133
- 'p',
134
- null,
135
- '(too many values to show)'
136
- ),
137
- showMenu && _react2.default.createElement(
138
- 'p',
139
- null,
140
- _react2.default.createElement('input', {
141
- type: 'text',
142
- placeholder: 'Filter values',
143
- className: 'pvtSearch',
144
- value: this.state.filterText,
145
- onChange: function onChange(e) {
146
- return _this2.setState({
147
- filterText: e.target.value
148
- });
164
+ {
165
+ role: 'button',
166
+ className: 'pvtButton',
167
+ onClick: function onClick() {
168
+ return removeValuesFromFilter(name, Object.keys(attrValues).filter(matchesFilter));
149
169
  }
150
- }),
151
- _react2.default.createElement('br', null),
152
- _react2.default.createElement(
153
- 'a',
154
- {
155
- role: 'button',
156
- className: 'pvtButton',
157
- onClick: function onClick() {
158
- return _this2.props.removeValuesFromFilter(_this2.props.name, Object.keys(_this2.props.attrValues).filter(_this2.matchesFilter.bind(_this2)));
159
- }
160
- },
161
- 'Select ',
162
- values.length === shown.length ? 'All' : shown.length
163
- ),
164
- ' ',
165
- _react2.default.createElement(
166
- 'a',
167
- {
168
- role: 'button',
169
- className: 'pvtButton',
170
- onClick: function onClick() {
171
- return _this2.props.addValuesToFilter(_this2.props.name, Object.keys(_this2.props.attrValues).filter(_this2.matchesFilter.bind(_this2)));
172
- }
173
- },
174
- 'Deselect ',
175
- values.length === shown.length ? 'All' : shown.length
176
- )
170
+ },
171
+ 'Select ',
172
+ values.length === shown.length ? 'All' : shown.length
177
173
  ),
178
- showMenu && _react2.default.createElement(
179
- 'div',
180
- { className: 'pvtCheckContainer' },
181
- shown.map(function (x) {
182
- return _react2.default.createElement(
183
- 'p',
184
- {
185
- key: x,
186
- onClick: function onClick() {
187
- return _this2.toggleValue(x);
188
- },
189
- className: x in _this2.props.valueFilter ? '' : 'selected'
190
- },
191
- _react2.default.createElement(
192
- 'a',
193
- { className: 'pvtOnly', onClick: function onClick(e) {
194
- return _this2.selectOnly(e, x);
195
- } },
196
- 'only'
197
- ),
198
- _react2.default.createElement(
199
- 'a',
200
- { className: 'pvtOnlySpacer' },
201
- '\xA0'
202
- ),
203
- x === '' ? _react2.default.createElement(
204
- 'em',
205
- null,
206
- 'null'
207
- ) : x
208
- );
209
- })
210
- )
211
- )
212
- );
213
- }
214
- }, {
215
- key: 'toggleFilterBox',
216
- value: function toggleFilterBox() {
217
- this.setState({ open: !this.state.open });
218
- this.props.moveFilterBoxToTop(this.props.name);
219
- }
220
- }, {
221
- key: 'render',
222
- value: function render() {
223
- var filtered = Object.keys(this.props.valueFilter).length !== 0 ? 'pvtFilteredAttribute' : '';
224
- return _react2.default.createElement(
225
- 'li',
226
- { 'data-id': this.props.name },
227
- _react2.default.createElement(
228
- 'span',
229
- { className: 'pvtAttr ' + filtered },
230
- this.props.name,
174
+ ' ',
231
175
  _react2.default.createElement(
232
- 'span',
176
+ 'a',
233
177
  {
234
- className: 'pvtTriangle',
235
- onClick: this.toggleFilterBox.bind(this)
178
+ role: 'button',
179
+ className: 'pvtButton',
180
+ onClick: function onClick() {
181
+ return addValuesToFilter(name, Object.keys(attrValues).filter(matchesFilter));
182
+ }
236
183
  },
237
- ' ',
238
- '\u25BE'
184
+ 'Deselect ',
185
+ values.length === shown.length ? 'All' : shown.length
239
186
  )
240
187
  ),
241
- this.state.open ? this.getFilterBox() : null
242
- );
243
- }
244
- }]);
245
-
246
- return DraggableAttribute;
247
- }(_react2.default.Component);
188
+ showMenu && _react2.default.createElement(
189
+ 'div',
190
+ { className: 'pvtCheckContainer' },
191
+ shown.map(function (x) {
192
+ return _react2.default.createElement(
193
+ 'p',
194
+ {
195
+ key: x,
196
+ onClick: function onClick() {
197
+ return toggleValue(x);
198
+ },
199
+ className: x in valueFilter ? '' : 'selected'
200
+ },
201
+ _react2.default.createElement(
202
+ 'a',
203
+ { className: 'pvtOnly', onClick: function onClick(e) {
204
+ return selectOnly(e, x);
205
+ } },
206
+ 'only'
207
+ ),
208
+ _react2.default.createElement(
209
+ 'a',
210
+ { className: 'pvtOnlySpacer' },
211
+ '\xA0'
212
+ ),
213
+ x === '' ? _react2.default.createElement(
214
+ 'em',
215
+ null,
216
+ 'null'
217
+ ) : x
218
+ );
219
+ })
220
+ )
221
+ )
222
+ );
223
+ };
224
+
225
+ var filtered = Object.keys(valueFilter).length !== 0 ? 'pvtFilteredAttribute' : '';
226
+
227
+ return _react2.default.createElement(
228
+ 'li',
229
+ { 'data-id': name },
230
+ _react2.default.createElement(
231
+ 'span',
232
+ { className: 'pvtAttr ' + filtered },
233
+ name,
234
+ _react2.default.createElement(
235
+ 'span',
236
+ { className: 'pvtTriangle', onClick: toggleFilterBox },
237
+ ' ',
238
+ '\u25BE'
239
+ )
240
+ ),
241
+ open ? getFilterBox() : null
242
+ );
243
+ }
248
244
 
249
245
  DraggableAttribute.defaultProps = {
250
246
  valueFilter: {}
@@ -262,533 +258,490 @@ DraggableAttribute.propTypes = {
262
258
  zIndex: _propTypes2.default.number
263
259
  };
264
260
 
265
- var Dropdown = exports.Dropdown = function (_React$PureComponent) {
266
- _inherits(Dropdown, _React$PureComponent);
267
-
268
- function Dropdown() {
269
- _classCallCheck(this, Dropdown);
270
-
271
- return _possibleConstructorReturn(this, (Dropdown.__proto__ || Object.getPrototypeOf(Dropdown)).apply(this, arguments));
272
- }
273
-
274
- _createClass(Dropdown, [{
275
- key: 'render',
276
- value: function render() {
277
- var _this4 = this;
278
-
279
- return _react2.default.createElement(
261
+ function Dropdown(props) {
262
+ return _react2.default.createElement(
263
+ 'div',
264
+ { className: 'pvtDropdown', style: { zIndex: props.zIndex } },
265
+ _react2.default.createElement(
266
+ 'div',
267
+ {
268
+ onClick: function onClick(e) {
269
+ e.stopPropagation();
270
+ props.toggle();
271
+ },
272
+ className: 'pvtDropdownValue pvtDropdownCurrent ' + (props.open ? 'pvtDropdownCurrentOpen' : ''),
273
+ role: 'button'
274
+ },
275
+ _react2.default.createElement(
280
276
  'div',
281
- { className: 'pvtDropdown', style: { zIndex: this.props.zIndex } },
282
- _react2.default.createElement(
277
+ { className: 'pvtDropdownIcon' },
278
+ props.open ? '×' : '▾'
279
+ ),
280
+ props.current || _react2.default.createElement(
281
+ 'span',
282
+ null,
283
+ '\xA0'
284
+ )
285
+ ),
286
+ props.open && _react2.default.createElement(
287
+ 'div',
288
+ { className: 'pvtDropdownMenu' },
289
+ props.values.map(function (r) {
290
+ return _react2.default.createElement(
283
291
  'div',
284
292
  {
293
+ key: r,
294
+ role: 'button',
285
295
  onClick: function onClick(e) {
286
296
  e.stopPropagation();
287
- _this4.props.toggle();
297
+ if (props.current === r) {
298
+ props.toggle();
299
+ } else {
300
+ props.setValue(r);
301
+ }
288
302
  },
289
- className: 'pvtDropdownValue pvtDropdownCurrent ' + (this.props.open ? 'pvtDropdownCurrentOpen' : ''),
290
- role: 'button'
303
+ className: 'pvtDropdownValue ' + (r === props.current ? 'pvtDropdownActiveValue' : '')
291
304
  },
292
- _react2.default.createElement(
293
- 'div',
294
- { className: 'pvtDropdownIcon' },
295
- this.props.open ? '×' : '▾'
296
- ),
297
- this.props.current || _react2.default.createElement(
298
- 'span',
299
- null,
300
- '\xA0'
301
- )
302
- ),
303
- this.props.open && _react2.default.createElement(
304
- 'div',
305
- { className: 'pvtDropdownMenu' },
306
- this.props.values.map(function (r) {
307
- return _react2.default.createElement(
308
- 'div',
309
- {
310
- key: r,
311
- role: 'button',
312
- onClick: function onClick(e) {
313
- e.stopPropagation();
314
- if (_this4.props.current === r) {
315
- _this4.props.toggle();
316
- } else {
317
- _this4.props.setValue(r);
318
- }
319
- },
320
- className: 'pvtDropdownValue ' + (r === _this4.props.current ? 'pvtDropdownActiveValue' : '')
321
- },
322
- r
323
- );
324
- })
325
- )
326
- );
327
- }
328
- }]);
329
-
330
- return Dropdown;
331
- }(_react2.default.PureComponent);
332
-
333
- var ConditionalFormattingUI = exports.ConditionalFormattingUI = function (_React$Component2) {
334
- _inherits(ConditionalFormattingUI, _React$Component2);
335
-
336
- function ConditionalFormattingUI(props) {
337
- _classCallCheck(this, ConditionalFormattingUI);
338
-
339
- var _this5 = _possibleConstructorReturn(this, (ConditionalFormattingUI.__proto__ || Object.getPrototypeOf(ConditionalFormattingUI)).call(this, props));
340
-
341
- _this5.state = {
342
- open: false
305
+ r
306
+ );
307
+ })
308
+ )
309
+ );
310
+ }
311
+
312
+ function ConditionalFormattingUI(props) {
313
+ var _useState5 = (0, _react.useState)(false),
314
+ _useState6 = _slicedToArray(_useState5, 2),
315
+ open = _useState6[0],
316
+ setOpen = _useState6[1];
317
+
318
+ var getConditionalFormatting = function getConditionalFormatting() {
319
+ var tableOptions = props.tableOptions || {};
320
+ return tableOptions.conditionalFormatting ? tableOptions.conditionalFormatting : { rules: [] };
321
+ };
322
+
323
+ var updateConditionalFormatting = function updateConditionalFormatting(conditionalFormatting) {
324
+ var currentTableOptions = props.tableOptions || {};
325
+ var tableOptions = Object.assign({}, currentTableOptions, {
326
+ conditionalFormatting: conditionalFormatting
327
+ });
328
+ props.onChange({
329
+ tableOptions: { $set: tableOptions }
330
+ });
331
+ };
332
+
333
+ var addRule = function addRule() {
334
+ var cf = getConditionalFormatting();
335
+ var newRule = {
336
+ condition: {
337
+ type: 'greaterThan',
338
+ value: 0
339
+ },
340
+ style: {
341
+ backgroundColor: '#FFFFE0'
342
+ }
343
343
  };
344
- return _this5;
345
- }
346
-
347
- _createClass(ConditionalFormattingUI, [{
348
- key: 'getConditionalFormatting',
349
- value: function getConditionalFormatting() {
350
- var tableOptions = this.props.tableOptions || {};
351
- return tableOptions.conditionalFormatting ? tableOptions.conditionalFormatting : { rules: [] };
352
- }
353
- }, {
354
- key: 'updateConditionalFormatting',
355
- value: function updateConditionalFormatting(conditionalFormatting) {
356
- var currentTableOptions = this.props.tableOptions || {};
357
- var tableOptions = Object.assign({}, currentTableOptions, {
358
- conditionalFormatting: conditionalFormatting
344
+ updateConditionalFormatting({
345
+ rules: cf.rules.slice().concat([newRule])
346
+ });
347
+ };
348
+
349
+ var removeRule = function removeRule(index) {
350
+ var cf = getConditionalFormatting();
351
+ var newRules = cf.rules.filter(function (_, i) {
352
+ return i !== index;
353
+ });
354
+ updateConditionalFormatting({
355
+ rules: newRules
356
+ });
357
+ };
358
+
359
+ var updateRule = function updateRule(index, field, value) {
360
+ var cf = getConditionalFormatting();
361
+ var newRules = cf.rules.slice();
362
+ if (field.startsWith('condition.')) {
363
+ var conditionField = field.replace('condition.', '');
364
+ var newCondition = Object.assign({}, newRules[index].condition);
365
+ if (value === null) {
366
+ delete newCondition[conditionField];
367
+ } else {
368
+ newCondition[conditionField] = value;
369
+ }
370
+ newRules[index] = Object.assign({}, newRules[index], {
371
+ condition: newCondition
359
372
  });
360
- this.props.onChange({
361
- tableOptions: { $set: tableOptions }
373
+ } else if (field.startsWith('style.')) {
374
+ var styleField = field.replace('style.', '');
375
+ var newStyle = Object.assign({}, newRules[index].style || {});
376
+ if (value === null || value === '') {
377
+ delete newStyle[styleField];
378
+ } else {
379
+ newStyle[styleField] = value;
380
+ }
381
+ newRules[index] = Object.assign({}, newRules[index], {
382
+ style: newStyle
362
383
  });
363
384
  }
364
- }, {
365
- key: 'addRule',
366
- value: function addRule() {
367
- var cf = this.getConditionalFormatting();
368
- var newRule = {
369
- condition: {
370
- type: 'greaterThan',
371
- value: 0
385
+ updateConditionalFormatting({
386
+ rules: newRules
387
+ });
388
+ };
389
+
390
+ var toggle = function toggle() {
391
+ setOpen(!open);
392
+ if (props.moveToTop) {
393
+ props.moveToTop();
394
+ }
395
+ };
396
+
397
+ var cf = getConditionalFormatting();
398
+ var conditionTypes = [{ value: 'greaterThan', label: 'Greater Than' }, { value: 'lessThan', label: 'Less Than' }, { value: 'greaterThanOrEqual', label: 'Greater Than Or Equal' }, { value: 'lessThanOrEqual', label: 'Less Than Or Equal' }, { value: 'equal', label: 'Equal' }, { value: 'notEqual', label: 'Not Equal' }, { value: 'empty', label: 'Empty' }, { value: 'notEmpty', label: 'Not Empty' }, { value: 'contains', label: 'Contains' }, { value: 'notContains', label: 'Not Contains' }];
399
+
400
+ var needsValue = function needsValue(type) {
401
+ return !['empty', 'notEmpty'].includes(type);
402
+ };
403
+
404
+ return _react2.default.createElement(
405
+ 'div',
406
+ null,
407
+ _react2.default.createElement(
408
+ 'button',
409
+ {
410
+ type: 'button',
411
+ className: 'pvtButton',
412
+ onClick: function onClick(e) {
413
+ e.stopPropagation();
414
+ toggle();
372
415
  },
416
+ style: { marginTop: '5px' }
417
+ },
418
+ open ? '▼' : '▶',
419
+ ' Conditional Formatting'
420
+ ),
421
+ open && _react2.default.createElement(
422
+ 'div',
423
+ {
424
+ className: 'pvtFilterBox',
373
425
  style: {
374
- backgroundColor: '#FFFFE0'
375
- }
376
- };
377
- this.updateConditionalFormatting({
378
- rules: cf.rules.slice().concat([newRule])
379
- });
380
- }
381
- }, {
382
- key: 'removeRule',
383
- value: function removeRule(index) {
384
- var cf = this.getConditionalFormatting();
385
- var newRules = cf.rules.filter(function (_, i) {
386
- return i !== index;
387
- });
388
- this.updateConditionalFormatting({
389
- rules: newRules
390
- });
391
- }
392
- }, {
393
- key: 'updateRule',
394
- value: function updateRule(index, field, value) {
395
- var cf = this.getConditionalFormatting();
396
- var newRules = cf.rules.slice();
397
- if (field.startsWith('condition.')) {
398
- var conditionField = field.replace('condition.', '');
399
- var newCondition = Object.assign({}, newRules[index].condition);
400
- if (value === null) {
401
- delete newCondition[conditionField];
402
- } else {
403
- newCondition[conditionField] = value;
404
- }
405
- newRules[index] = Object.assign({}, newRules[index], {
406
- condition: newCondition
407
- });
408
- } else if (field.startsWith('style.')) {
409
- var styleField = field.replace('style.', '');
410
- var newStyle = Object.assign({}, newRules[index].style || {});
411
- if (value === null || value === '') {
412
- delete newStyle[styleField];
413
- } else {
414
- newStyle[styleField] = value;
426
+ display: 'block',
427
+ cursor: 'initial',
428
+ zIndex: props.zIndex,
429
+ marginTop: '5px',
430
+ minWidth: '400px',
431
+ position: 'relative'
432
+ },
433
+ onClick: function onClick() {
434
+ return props.moveToTop && props.moveToTop();
415
435
  }
416
- newRules[index] = Object.assign({}, newRules[index], {
417
- style: newStyle
418
- });
419
- }
420
- this.updateConditionalFormatting({
421
- rules: newRules
422
- });
423
- }
424
- }, {
425
- key: 'toggle',
426
- value: function toggle() {
427
- this.setState({ open: !this.state.open });
428
- if (this.props.moveToTop) {
429
- this.props.moveToTop();
430
- }
431
- }
432
- }, {
433
- key: 'render',
434
- value: function render() {
435
- var _this6 = this;
436
-
437
- var cf = this.getConditionalFormatting();
438
- var conditionTypes = [{ value: 'greaterThan', label: 'Greater Than' }, { value: 'lessThan', label: 'Less Than' }, { value: 'greaterThanOrEqual', label: 'Greater Than Or Equal' }, { value: 'lessThanOrEqual', label: 'Less Than Or Equal' }, { value: 'equal', label: 'Equal' }, { value: 'notEqual', label: 'Not Equal' }, { value: 'empty', label: 'Empty' }, { value: 'notEmpty', label: 'Not Empty' }, { value: 'contains', label: 'Contains' }, { value: 'notContains', label: 'Not Contains' }];
439
-
440
- var needsValue = function needsValue(type) {
441
- return !['empty', 'notEmpty'].includes(type);
442
- };
443
-
444
- return _react2.default.createElement(
445
- 'div',
436
+ },
437
+ _react2.default.createElement(
438
+ 'a',
439
+ { onClick: function onClick() {
440
+ return setOpen(false);
441
+ }, className: 'pvtCloseX' },
442
+ '\xD7'
443
+ ),
444
+ _react2.default.createElement(
445
+ 'h4',
446
446
  null,
447
- _react2.default.createElement(
448
- 'button',
449
- {
450
- type: 'button',
451
- className: 'pvtButton',
452
- onClick: function onClick(e) {
453
- e.stopPropagation();
454
- _this6.toggle();
455
- },
456
- style: { marginTop: '5px' }
457
- },
458
- this.state.open ? '▼' : '▶',
459
- ' Conditional Formatting'
460
- ),
461
- this.state.open && _react2.default.createElement(
447
+ 'Conditional Formatting Rules'
448
+ ),
449
+ cf.rules.length === 0 && _react2.default.createElement(
450
+ 'p',
451
+ { style: { fontStyle: 'italic', color: '#666' } },
452
+ 'No rules defined. Add a rule to format cells based on conditions.'
453
+ ),
454
+ cf.rules.map(function (rule, index) {
455
+ return _react2.default.createElement(
462
456
  'div',
463
457
  {
464
- className: 'pvtFilterBox',
458
+ key: index,
465
459
  style: {
466
- display: 'block',
467
- cursor: 'initial',
468
- zIndex: this.props.zIndex,
469
- marginTop: '5px',
470
- minWidth: '400px',
471
- position: 'relative'
472
- },
473
- onClick: function onClick() {
474
- return _this6.props.moveToTop && _this6.props.moveToTop();
460
+ border: '1px solid #c8d4e3',
461
+ padding: '10px',
462
+ marginBottom: '10px',
463
+ backgroundColor: '#f9f9f9'
475
464
  }
476
465
  },
477
466
  _react2.default.createElement(
478
- 'a',
467
+ 'div',
479
468
  {
480
- onClick: function onClick() {
481
- return _this6.setState({ open: false });
482
- },
483
- className: 'pvtCloseX'
469
+ style: {
470
+ display: 'flex',
471
+ justifyContent: 'space-between',
472
+ marginBottom: '8px'
473
+ }
484
474
  },
485
- '\xD7'
475
+ _react2.default.createElement(
476
+ 'strong',
477
+ null,
478
+ 'Rule ',
479
+ index + 1
480
+ ),
481
+ _react2.default.createElement(
482
+ 'button',
483
+ {
484
+ type: 'button',
485
+ className: 'pvtButton',
486
+ onClick: function onClick(e) {
487
+ e.stopPropagation();
488
+ removeRule(index);
489
+ },
490
+ style: { fontSize: '10px', padding: '2px 6px' }
491
+ },
492
+ 'Remove'
493
+ )
486
494
  ),
487
495
  _react2.default.createElement(
488
- 'h4',
489
- null,
490
- 'Conditional Formatting Rules'
491
- ),
492
- cf.rules.length === 0 && _react2.default.createElement(
493
- 'p',
494
- { style: { fontStyle: 'italic', color: '#666' } },
495
- 'No rules defined. Add a rule to format cells based on conditions.'
496
- ),
497
- cf.rules.map(function (rule, index) {
498
- return _react2.default.createElement(
499
- 'div',
496
+ 'div',
497
+ {
498
+ style: { marginBottom: '8px' },
499
+ onClick: function onClick(e) {
500
+ return e.stopPropagation();
501
+ },
502
+ onMouseDown: function onMouseDown(e) {
503
+ return e.stopPropagation();
504
+ }
505
+ },
506
+ _react2.default.createElement(
507
+ 'label',
500
508
  {
501
- key: index,
502
509
  style: {
503
- border: '1px solid #c8d4e3',
504
- padding: '10px',
505
- marginBottom: '10px',
506
- backgroundColor: '#f9f9f9'
510
+ display: 'block',
511
+ marginBottom: '4px',
512
+ fontSize: '11px'
507
513
  }
508
514
  },
509
- _react2.default.createElement(
510
- 'div',
511
- {
512
- style: {
513
- display: 'flex',
514
- justifyContent: 'space-between',
515
- marginBottom: '8px'
516
- }
517
- },
518
- _react2.default.createElement(
519
- 'strong',
520
- null,
521
- 'Rule ',
522
- index + 1
523
- ),
524
- _react2.default.createElement(
525
- 'button',
526
- {
527
- type: 'button',
528
- className: 'pvtButton',
529
- onClick: function onClick(e) {
530
- e.stopPropagation();
531
- _this6.removeRule(index);
532
- },
533
- style: { fontSize: '10px', padding: '2px 6px' }
534
- },
535
- 'Remove'
536
- )
537
- ),
538
- _react2.default.createElement(
539
- 'div',
540
- {
541
- style: { marginBottom: '8px' },
542
- onClick: function onClick(e) {
543
- return e.stopPropagation();
544
- },
545
- onMouseDown: function onMouseDown(e) {
546
- return e.stopPropagation();
547
- }
548
- },
549
- _react2.default.createElement(
550
- 'label',
551
- {
552
- style: {
553
- display: 'block',
554
- marginBottom: '4px',
555
- fontSize: '11px'
556
- }
557
- },
558
- 'Condition Type:'
559
- ),
560
- _react2.default.createElement(
561
- 'select',
562
- {
563
- value: rule.condition.type || 'greaterThan',
564
- onChange: function onChange(e) {
565
- e.stopPropagation();
566
- var newType = e.target.value;
567
- var cf = _this6.getConditionalFormatting();
568
- var newRules = cf.rules.slice();
569
- var newCondition = Object.assign({}, newRules[index].condition, {
570
- type: newType
571
- });
572
- // Remove value field if condition type doesn't need it
573
- if (!needsValue(newType)) {
574
- delete newCondition.value;
575
- }
576
- newRules[index] = Object.assign({}, newRules[index], {
577
- condition: newCondition
578
- });
579
- _this6.updateConditionalFormatting({
580
- rules: newRules
581
- });
582
- },
583
- style: { width: '100%', padding: '4px' }
584
- },
585
- conditionTypes.map(function (ct) {
586
- return _react2.default.createElement(
587
- 'option',
588
- { key: ct.value, value: ct.value },
589
- ct.label
590
- );
591
- })
592
- )
593
- ),
594
- needsValue(rule.condition.type) && _react2.default.createElement(
595
- 'div',
596
- { style: { marginBottom: '8px' } },
597
- _react2.default.createElement(
598
- 'label',
599
- {
600
- style: {
601
- display: 'block',
602
- marginBottom: '4px',
603
- fontSize: '11px'
604
- }
605
- },
606
- 'Condition Value:'
607
- ),
608
- _react2.default.createElement('input', {
609
- type: 'text',
610
- value: typeof rule.condition.value !== 'undefined' && rule.condition.value !== null ? rule.condition.value : '',
611
- onChange: function onChange(e) {
612
- e.stopPropagation();
613
- var val = e.target.value;
614
- // Try to parse as number, otherwise use as string
615
- var numVal = val === '' ? null : isNaN(val) ? val : parseFloat(val);
616
- _this6.updateRule(index, 'condition.value', numVal);
617
- },
618
- placeholder: 'Enter value',
619
- style: { width: '100%', padding: '4px' }
620
- })
621
- ),
622
- _react2.default.createElement(
623
- 'div',
624
- { style: { marginBottom: '8px' } },
625
- _react2.default.createElement(
626
- 'label',
627
- {
628
- style: {
629
- display: 'block',
630
- marginBottom: '4px',
631
- fontSize: '11px'
632
- }
633
- },
634
- 'Background Color:'
635
- ),
636
- _react2.default.createElement('input', {
637
- type: 'color',
638
- value: rule.style && rule.style.backgroundColor ? rule.style.backgroundColor : '#FFFFFF',
639
- onChange: function onChange(e) {
640
- e.stopPropagation();
641
- _this6.updateRule(index, 'style.backgroundColor', e.target.value);
642
- },
643
- style: { width: '100%', padding: '2px' }
644
- })
645
- ),
646
- _react2.default.createElement(
647
- 'div',
648
- { style: { marginBottom: '8px' } },
649
- _react2.default.createElement(
650
- 'label',
651
- {
652
- style: {
653
- display: 'block',
654
- marginBottom: '4px',
655
- fontSize: '11px'
656
- }
657
- },
658
- 'Text Color:'
659
- ),
660
- _react2.default.createElement('input', {
661
- type: 'color',
662
- value: rule.style && rule.style.color ? rule.style.color : '#000000',
663
- onChange: function onChange(e) {
664
- e.stopPropagation();
665
- _this6.updateRule(index, 'style.color', e.target.value);
666
- },
667
- style: { width: '100%', padding: '2px' }
668
- })
669
- ),
670
- _react2.default.createElement(
671
- 'div',
672
- {
673
- style: { marginBottom: '8px' },
674
- onClick: function onClick(e) {
675
- return e.stopPropagation();
676
- },
677
- onMouseDown: function onMouseDown(e) {
678
- return e.stopPropagation();
679
- }
680
- },
681
- _react2.default.createElement(
682
- 'label',
683
- {
684
- style: {
685
- display: 'block',
686
- marginBottom: '4px',
687
- fontSize: '11px'
688
- }
689
- },
690
- 'Font Weight:'
691
- ),
692
- _react2.default.createElement(
693
- 'select',
694
- {
695
- value: rule.style && rule.style.fontWeight ? rule.style.fontWeight : '',
696
- onChange: function onChange(e) {
697
- e.stopPropagation();
698
- _this6.updateRule(index, 'style.fontWeight', e.target.value || null);
699
- },
700
- style: { width: '100%', padding: '4px' }
701
- },
702
- _react2.default.createElement(
703
- 'option',
704
- { value: '' },
705
- 'Normal'
706
- ),
707
- _react2.default.createElement(
708
- 'option',
709
- { value: 'bold' },
710
- 'Bold'
711
- ),
712
- _react2.default.createElement(
713
- 'option',
714
- { value: 'lighter' },
715
- 'Lighter'
716
- )
717
- )
718
- ),
719
- _react2.default.createElement(
720
- 'div',
721
- {
722
- style: { marginBottom: '8px' },
723
- onClick: function onClick(e) {
724
- return e.stopPropagation();
725
- },
726
- onMouseDown: function onMouseDown(e) {
727
- return e.stopPropagation();
515
+ 'Condition Type:'
516
+ ),
517
+ _react2.default.createElement(
518
+ 'select',
519
+ {
520
+ value: rule.condition.type || 'greaterThan',
521
+ onChange: function onChange(e) {
522
+ e.stopPropagation();
523
+ var newType = e.target.value;
524
+ var cfNow = getConditionalFormatting();
525
+ var newRules = cfNow.rules.slice();
526
+ var newCondition = Object.assign({}, newRules[index].condition, {
527
+ type: newType
528
+ });
529
+ // Remove value field if condition type doesn't need it
530
+ if (!needsValue(newType)) {
531
+ delete newCondition.value;
728
532
  }
533
+ newRules[index] = Object.assign({}, newRules[index], {
534
+ condition: newCondition
535
+ });
536
+ updateConditionalFormatting({
537
+ rules: newRules
538
+ });
729
539
  },
730
- _react2.default.createElement(
731
- 'label',
732
- {
733
- style: {
734
- display: 'block',
735
- marginBottom: '4px',
736
- fontSize: '11px'
737
- }
738
- },
739
- 'Font Style:'
740
- ),
741
- _react2.default.createElement(
742
- 'select',
743
- {
744
- value: rule.style && rule.style.fontStyle ? rule.style.fontStyle : '',
745
- onChange: function onChange(e) {
746
- e.stopPropagation();
747
- _this6.updateRule(index, 'style.fontStyle', e.target.value || null);
748
- },
749
- style: { width: '100%', padding: '4px' }
750
- },
751
- _react2.default.createElement(
752
- 'option',
753
- { value: '' },
754
- 'Normal'
755
- ),
756
- _react2.default.createElement(
757
- 'option',
758
- { value: 'italic' },
759
- 'Italic'
760
- ),
761
- _react2.default.createElement(
762
- 'option',
763
- { value: 'oblique' },
764
- 'Oblique'
765
- )
766
- )
767
- )
768
- );
769
- }),
770
- _react2.default.createElement(
771
- 'button',
772
- {
773
- type: 'button',
774
- className: 'pvtButton',
775
- onClick: function onClick(e) {
540
+ style: { width: '100%', padding: '4px' }
541
+ },
542
+ conditionTypes.map(function (ct) {
543
+ return _react2.default.createElement(
544
+ 'option',
545
+ { key: ct.value, value: ct.value },
546
+ ct.label
547
+ );
548
+ })
549
+ )
550
+ ),
551
+ needsValue(rule.condition.type) && _react2.default.createElement(
552
+ 'div',
553
+ { style: { marginBottom: '8px' } },
554
+ _react2.default.createElement(
555
+ 'label',
556
+ {
557
+ style: {
558
+ display: 'block',
559
+ marginBottom: '4px',
560
+ fontSize: '11px'
561
+ }
562
+ },
563
+ 'Condition Value:'
564
+ ),
565
+ _react2.default.createElement('input', {
566
+ type: 'text',
567
+ value: typeof rule.condition.value !== 'undefined' && rule.condition.value !== null ? rule.condition.value : '',
568
+ onChange: function onChange(e) {
776
569
  e.stopPropagation();
777
- _this6.addRule();
570
+ var val = e.target.value;
571
+ // Try to parse as number, otherwise use as string
572
+ var numVal = val === '' ? null : isNaN(val) ? val : parseFloat(val);
573
+ updateRule(index, 'condition.value', numVal);
778
574
  },
779
- style: { width: '100%', marginTop: '10px' }
780
- },
781
- '+ Add Rule'
782
- )
783
- )
784
- );
785
- }
786
- }]);
787
-
788
- return ConditionalFormattingUI;
789
- }(_react2.default.Component);
790
-
791
- ConditionalFormattingUI.propTypes = {
575
+ placeholder: 'Enter value',
576
+ style: { width: '100%', padding: '4px' }
577
+ })
578
+ ),
579
+ _react2.default.createElement(
580
+ 'div',
581
+ { style: { marginBottom: '8px' } },
582
+ _react2.default.createElement(
583
+ 'label',
584
+ {
585
+ style: {
586
+ display: 'block',
587
+ marginBottom: '4px',
588
+ fontSize: '11px'
589
+ }
590
+ },
591
+ 'Background Color:'
592
+ ),
593
+ _react2.default.createElement('input', {
594
+ type: 'color',
595
+ value: rule.style && rule.style.backgroundColor ? rule.style.backgroundColor : '#FFFFFF',
596
+ onChange: function onChange(e) {
597
+ e.stopPropagation();
598
+ updateRule(index, 'style.backgroundColor', e.target.value);
599
+ },
600
+ style: { width: '100%', padding: '2px' }
601
+ })
602
+ ),
603
+ _react2.default.createElement(
604
+ 'div',
605
+ { style: { marginBottom: '8px' } },
606
+ _react2.default.createElement(
607
+ 'label',
608
+ {
609
+ style: {
610
+ display: 'block',
611
+ marginBottom: '4px',
612
+ fontSize: '11px'
613
+ }
614
+ },
615
+ 'Text Color:'
616
+ ),
617
+ _react2.default.createElement('input', {
618
+ type: 'color',
619
+ value: rule.style && rule.style.color ? rule.style.color : '#000000',
620
+ onChange: function onChange(e) {
621
+ e.stopPropagation();
622
+ updateRule(index, 'style.color', e.target.value);
623
+ },
624
+ style: { width: '100%', padding: '2px' }
625
+ })
626
+ ),
627
+ _react2.default.createElement(
628
+ 'div',
629
+ {
630
+ style: { marginBottom: '8px' },
631
+ onClick: function onClick(e) {
632
+ return e.stopPropagation();
633
+ },
634
+ onMouseDown: function onMouseDown(e) {
635
+ return e.stopPropagation();
636
+ }
637
+ },
638
+ _react2.default.createElement(
639
+ 'label',
640
+ {
641
+ style: {
642
+ display: 'block',
643
+ marginBottom: '4px',
644
+ fontSize: '11px'
645
+ }
646
+ },
647
+ 'Font Weight:'
648
+ ),
649
+ _react2.default.createElement(
650
+ 'select',
651
+ {
652
+ value: rule.style && rule.style.fontWeight ? rule.style.fontWeight : '',
653
+ onChange: function onChange(e) {
654
+ e.stopPropagation();
655
+ updateRule(index, 'style.fontWeight', e.target.value || null);
656
+ },
657
+ style: { width: '100%', padding: '4px' }
658
+ },
659
+ _react2.default.createElement(
660
+ 'option',
661
+ { value: '' },
662
+ 'Normal'
663
+ ),
664
+ _react2.default.createElement(
665
+ 'option',
666
+ { value: 'bold' },
667
+ 'Bold'
668
+ ),
669
+ _react2.default.createElement(
670
+ 'option',
671
+ { value: 'lighter' },
672
+ 'Lighter'
673
+ )
674
+ )
675
+ ),
676
+ _react2.default.createElement(
677
+ 'div',
678
+ {
679
+ style: { marginBottom: '8px' },
680
+ onClick: function onClick(e) {
681
+ return e.stopPropagation();
682
+ },
683
+ onMouseDown: function onMouseDown(e) {
684
+ return e.stopPropagation();
685
+ }
686
+ },
687
+ _react2.default.createElement(
688
+ 'label',
689
+ {
690
+ style: {
691
+ display: 'block',
692
+ marginBottom: '4px',
693
+ fontSize: '11px'
694
+ }
695
+ },
696
+ 'Font Style:'
697
+ ),
698
+ _react2.default.createElement(
699
+ 'select',
700
+ {
701
+ value: rule.style && rule.style.fontStyle ? rule.style.fontStyle : '',
702
+ onChange: function onChange(e) {
703
+ e.stopPropagation();
704
+ updateRule(index, 'style.fontStyle', e.target.value || null);
705
+ },
706
+ style: { width: '100%', padding: '4px' }
707
+ },
708
+ _react2.default.createElement(
709
+ 'option',
710
+ { value: '' },
711
+ 'Normal'
712
+ ),
713
+ _react2.default.createElement(
714
+ 'option',
715
+ { value: 'italic' },
716
+ 'Italic'
717
+ ),
718
+ _react2.default.createElement(
719
+ 'option',
720
+ { value: 'oblique' },
721
+ 'Oblique'
722
+ )
723
+ )
724
+ )
725
+ );
726
+ }),
727
+ _react2.default.createElement(
728
+ 'button',
729
+ {
730
+ type: 'button',
731
+ className: 'pvtButton',
732
+ onClick: function onClick(e) {
733
+ e.stopPropagation();
734
+ addRule();
735
+ },
736
+ style: { width: '100%', marginTop: '10px' }
737
+ },
738
+ '+ Add Rule'
739
+ )
740
+ )
741
+ );
742
+ }
743
+
744
+ ConditionalFormattingUI.propTypes = {
792
745
  tableOptions: _propTypes2.default.object,
793
746
  onChange: _propTypes2.default.func.isRequired,
794
747
  moveToTop: _propTypes2.default.func,
@@ -801,374 +754,348 @@ ConditionalFormattingUI.defaultProps = {
801
754
  zIndex: 1
802
755
  };
803
756
 
804
- var CellFormattingUI = exports.CellFormattingUI = function (_React$Component3) {
805
- _inherits(CellFormattingUI, _React$Component3);
806
-
807
- function CellFormattingUI(props) {
808
- _classCallCheck(this, CellFormattingUI);
809
-
810
- var _this7 = _possibleConstructorReturn(this, (CellFormattingUI.__proto__ || Object.getPrototypeOf(CellFormattingUI)).call(this, props));
811
-
812
- _this7.state = {
813
- open: false
757
+ function CellFormattingUI(props) {
758
+ var _useState7 = (0, _react.useState)(false),
759
+ _useState8 = _slicedToArray(_useState7, 2),
760
+ open = _useState8[0],
761
+ setOpen = _useState8[1];
762
+
763
+ var getCellFormatting = function getCellFormatting() {
764
+ var tableOptions = props.tableOptions || {};
765
+ return tableOptions.cellFormatting ? tableOptions.cellFormatting : { rules: [] };
766
+ };
767
+
768
+ var updateCellFormatting = function updateCellFormatting(cellFormatting) {
769
+ var currentTableOptions = props.tableOptions || {};
770
+ var tableOptions = Object.assign({}, currentTableOptions, {
771
+ cellFormatting: cellFormatting
772
+ });
773
+ props.onChange({
774
+ tableOptions: { $set: tableOptions }
775
+ });
776
+ };
777
+
778
+ var addRule = function addRule() {
779
+ var cf = getCellFormatting();
780
+ var newRule = {
781
+ format: {
782
+ thousandsSep: ',',
783
+ decimalSep: '.',
784
+ decimalPlaces: 2,
785
+ prefix: '',
786
+ suffix: ''
787
+ }
814
788
  };
815
- return _this7;
816
- }
817
-
818
- _createClass(CellFormattingUI, [{
819
- key: 'getCellFormatting',
820
- value: function getCellFormatting() {
821
- var tableOptions = this.props.tableOptions || {};
822
- return tableOptions.cellFormatting ? tableOptions.cellFormatting : { rules: [] };
823
- }
824
- }, {
825
- key: 'updateCellFormatting',
826
- value: function updateCellFormatting(cellFormatting) {
827
- var currentTableOptions = this.props.tableOptions || {};
828
- var tableOptions = Object.assign({}, currentTableOptions, {
829
- cellFormatting: cellFormatting
830
- });
831
- this.props.onChange({
832
- tableOptions: { $set: tableOptions }
833
- });
834
- }
835
- }, {
836
- key: 'addRule',
837
- value: function addRule() {
838
- var cf = this.getCellFormatting();
839
- var newRule = {
840
- format: {
841
- thousandsSep: ',',
842
- decimalSep: '.',
843
- decimalPlaces: 2,
844
- prefix: '',
845
- suffix: ''
846
- }
847
- };
848
- this.updateCellFormatting({
849
- rules: cf.rules.slice().concat([newRule])
850
- });
851
- }
852
- }, {
853
- key: 'removeRule',
854
- value: function removeRule(index) {
855
- var cf = this.getCellFormatting();
856
- var newRules = cf.rules.filter(function (_, i) {
857
- return i !== index;
858
- });
859
- this.updateCellFormatting({
860
- rules: newRules
861
- });
862
- }
863
- }, {
864
- key: 'updateRule',
865
- value: function updateRule(index, field, value) {
866
- var cf = this.getCellFormatting();
867
- var newRules = cf.rules.slice();
868
- if (field.startsWith('format.')) {
869
- var formatField = field.replace('format.', '');
870
- var newFormat = Object.assign({}, newRules[index].format || {});
871
- if (value === null || value === '') {
872
- delete newFormat[formatField];
873
- } else {
874
- newFormat[formatField] = value;
875
- }
876
- newRules[index] = Object.assign({}, newRules[index], {
877
- format: newFormat
878
- });
789
+ updateCellFormatting({
790
+ rules: cf.rules.slice().concat([newRule])
791
+ });
792
+ };
793
+
794
+ var removeRule = function removeRule(index) {
795
+ var cf = getCellFormatting();
796
+ var newRules = cf.rules.filter(function (_, i) {
797
+ return i !== index;
798
+ });
799
+ updateCellFormatting({
800
+ rules: newRules
801
+ });
802
+ };
803
+
804
+ var updateRule = function updateRule(index, field, value) {
805
+ var cf = getCellFormatting();
806
+ var newRules = cf.rules.slice();
807
+ if (field.startsWith('format.')) {
808
+ var formatField = field.replace('format.', '');
809
+ var newFormat = Object.assign({}, newRules[index].format || {});
810
+ if (value === null || value === '') {
811
+ delete newFormat[formatField];
879
812
  } else {
880
- newRules[index] = Object.assign({}, newRules[index], _defineProperty({}, field, value));
813
+ newFormat[formatField] = value;
881
814
  }
882
- this.updateCellFormatting({
883
- rules: newRules
815
+ newRules[index] = Object.assign({}, newRules[index], {
816
+ format: newFormat
884
817
  });
818
+ } else {
819
+ newRules[index] = Object.assign({}, newRules[index], _defineProperty({}, field, value));
885
820
  }
886
- }, {
887
- key: 'toggle',
888
- value: function toggle() {
889
- this.setState({ open: !this.state.open });
890
- if (this.props.moveToTop) {
891
- this.props.moveToTop();
892
- }
821
+ updateCellFormatting({
822
+ rules: newRules
823
+ });
824
+ };
825
+
826
+ var toggle = function toggle() {
827
+ setOpen(!open);
828
+ if (props.moveToTop) {
829
+ props.moveToTop();
893
830
  }
894
- }, {
895
- key: 'render',
896
- value: function render() {
897
- var _this8 = this;
898
-
899
- var cf = this.getCellFormatting();
900
-
901
- return _react2.default.createElement(
902
- 'div',
831
+ };
832
+
833
+ var cf = getCellFormatting();
834
+
835
+ return _react2.default.createElement(
836
+ 'div',
837
+ null,
838
+ _react2.default.createElement(
839
+ 'button',
840
+ {
841
+ type: 'button',
842
+ className: 'pvtButton',
843
+ onClick: function onClick(e) {
844
+ e.stopPropagation();
845
+ toggle();
846
+ },
847
+ style: { marginTop: '5px' }
848
+ },
849
+ open ? '▼' : '▶',
850
+ ' Cell Formatting'
851
+ ),
852
+ open && _react2.default.createElement(
853
+ 'div',
854
+ {
855
+ className: 'pvtFilterBox',
856
+ style: {
857
+ display: 'block',
858
+ cursor: 'initial',
859
+ zIndex: props.zIndex,
860
+ marginTop: '5px',
861
+ minWidth: '400px',
862
+ position: 'relative'
863
+ },
864
+ onClick: function onClick() {
865
+ return props.moveToTop && props.moveToTop();
866
+ }
867
+ },
868
+ _react2.default.createElement(
869
+ 'a',
870
+ { onClick: function onClick() {
871
+ return setOpen(false);
872
+ }, className: 'pvtCloseX' },
873
+ '\xD7'
874
+ ),
875
+ _react2.default.createElement(
876
+ 'h4',
903
877
  null,
904
- _react2.default.createElement(
905
- 'button',
906
- {
907
- type: 'button',
908
- className: 'pvtButton',
909
- onClick: function onClick(e) {
910
- e.stopPropagation();
911
- _this8.toggle();
912
- },
913
- style: { marginTop: '5px' }
914
- },
915
- this.state.open ? '▼' : '▶',
916
- ' Cell Formatting'
917
- ),
918
- this.state.open && _react2.default.createElement(
878
+ 'Cell Formatting Rules'
879
+ ),
880
+ cf.rules.length === 0 && _react2.default.createElement(
881
+ 'p',
882
+ { style: { fontStyle: 'italic', color: '#666' } },
883
+ 'No rules defined. Add a rule to format cell values.'
884
+ ),
885
+ cf.rules.map(function (rule, index) {
886
+ return _react2.default.createElement(
919
887
  'div',
920
888
  {
921
- className: 'pvtFilterBox',
889
+ key: index,
922
890
  style: {
923
- display: 'block',
924
- cursor: 'initial',
925
- zIndex: this.props.zIndex,
926
- marginTop: '5px',
927
- minWidth: '400px',
928
- position: 'relative'
929
- },
930
- onClick: function onClick() {
931
- return _this8.props.moveToTop && _this8.props.moveToTop();
891
+ border: '1px solid #c8d4e3',
892
+ padding: '10px',
893
+ marginBottom: '10px',
894
+ backgroundColor: '#f9f9f9'
932
895
  }
933
896
  },
934
897
  _react2.default.createElement(
935
- 'a',
898
+ 'div',
936
899
  {
937
- onClick: function onClick() {
938
- return _this8.setState({ open: false });
939
- },
940
- className: 'pvtCloseX'
900
+ style: {
901
+ display: 'flex',
902
+ justifyContent: 'space-between',
903
+ marginBottom: '8px'
904
+ }
941
905
  },
942
- '\xD7'
906
+ _react2.default.createElement(
907
+ 'strong',
908
+ null,
909
+ 'Rule ',
910
+ index + 1
911
+ ),
912
+ _react2.default.createElement(
913
+ 'button',
914
+ {
915
+ type: 'button',
916
+ className: 'pvtButton',
917
+ onClick: function onClick(e) {
918
+ e.stopPropagation();
919
+ removeRule(index);
920
+ },
921
+ style: { fontSize: '10px', padding: '2px 6px' }
922
+ },
923
+ 'Remove'
924
+ )
943
925
  ),
944
926
  _react2.default.createElement(
945
- 'h4',
946
- null,
947
- 'Cell Formatting Rules'
927
+ 'div',
928
+ { style: { marginBottom: '8px' } },
929
+ _react2.default.createElement(
930
+ 'label',
931
+ {
932
+ style: {
933
+ display: 'block',
934
+ marginBottom: '4px',
935
+ fontSize: '11px'
936
+ }
937
+ },
938
+ 'Thousand Separator (empty = no separator, decimal formatting still applies):'
939
+ ),
940
+ _react2.default.createElement('input', {
941
+ type: 'text',
942
+ value: rule.format && 'thousandsSep' in rule.format ? rule.format.thousandsSep : '',
943
+ onChange: function onChange(e) {
944
+ e.stopPropagation();
945
+ updateRule(index, 'format.thousandsSep', e.target.value);
946
+ },
947
+ placeholder: ',',
948
+ maxLength: '1',
949
+ style: { width: '100%', padding: '4px' }
950
+ })
948
951
  ),
949
- cf.rules.length === 0 && _react2.default.createElement(
950
- 'p',
951
- { style: { fontStyle: 'italic', color: '#666' } },
952
- 'No rules defined. Add a rule to format cell values.'
952
+ _react2.default.createElement(
953
+ 'div',
954
+ { style: { marginBottom: '8px' } },
955
+ _react2.default.createElement(
956
+ 'label',
957
+ {
958
+ style: {
959
+ display: 'block',
960
+ marginBottom: '4px',
961
+ fontSize: '11px'
962
+ }
963
+ },
964
+ 'Decimal Separator:'
965
+ ),
966
+ _react2.default.createElement('input', {
967
+ type: 'text',
968
+ value: rule.format && 'decimalSep' in rule.format ? rule.format.decimalSep : '.',
969
+ onChange: function onChange(e) {
970
+ e.stopPropagation();
971
+ updateRule(index, 'format.decimalSep', e.target.value);
972
+ },
973
+ placeholder: '.',
974
+ maxLength: '1',
975
+ style: { width: '100%', padding: '4px' }
976
+ })
953
977
  ),
954
- cf.rules.map(function (rule, index) {
955
- return _react2.default.createElement(
956
- 'div',
978
+ _react2.default.createElement(
979
+ 'div',
980
+ { style: { marginBottom: '8px' } },
981
+ _react2.default.createElement(
982
+ 'label',
957
983
  {
958
- key: index,
959
984
  style: {
960
- border: '1px solid #c8d4e3',
961
- padding: '10px',
962
- marginBottom: '10px',
963
- backgroundColor: '#f9f9f9'
985
+ display: 'block',
986
+ marginBottom: '4px',
987
+ fontSize: '11px'
964
988
  }
965
989
  },
966
- _react2.default.createElement(
967
- 'div',
968
- {
969
- style: {
970
- display: 'flex',
971
- justifyContent: 'space-between',
972
- marginBottom: '8px'
973
- }
974
- },
975
- _react2.default.createElement(
976
- 'strong',
977
- null,
978
- 'Rule ',
979
- index + 1
980
- ),
981
- _react2.default.createElement(
982
- 'button',
983
- {
984
- type: 'button',
985
- className: 'pvtButton',
986
- onClick: function onClick(e) {
987
- e.stopPropagation();
988
- _this8.removeRule(index);
989
- },
990
- style: { fontSize: '10px', padding: '2px 6px' }
991
- },
992
- 'Remove'
993
- )
994
- ),
995
- _react2.default.createElement(
996
- 'div',
997
- { style: { marginBottom: '8px' } },
998
- _react2.default.createElement(
999
- 'label',
1000
- {
1001
- style: {
1002
- display: 'block',
1003
- marginBottom: '4px',
1004
- fontSize: '11px'
1005
- }
1006
- },
1007
- 'Thousand Separator (empty = no separator, decimal formatting still applies):'
1008
- ),
1009
- _react2.default.createElement('input', {
1010
- type: 'text',
1011
- value: rule.format && 'thousandsSep' in rule.format ? rule.format.thousandsSep : '',
1012
- onChange: function onChange(e) {
1013
- e.stopPropagation();
1014
- _this8.updateRule(index, 'format.thousandsSep', e.target.value);
1015
- },
1016
- placeholder: ',',
1017
- maxLength: '1',
1018
- style: { width: '100%', padding: '4px' }
1019
- })
1020
- ),
1021
- _react2.default.createElement(
1022
- 'div',
1023
- { style: { marginBottom: '8px' } },
1024
- _react2.default.createElement(
1025
- 'label',
1026
- {
1027
- style: {
1028
- display: 'block',
1029
- marginBottom: '4px',
1030
- fontSize: '11px'
1031
- }
1032
- },
1033
- 'Decimal Separator:'
1034
- ),
1035
- _react2.default.createElement('input', {
1036
- type: 'text',
1037
- value: rule.format && 'decimalSep' in rule.format ? rule.format.decimalSep : '.',
1038
- onChange: function onChange(e) {
1039
- e.stopPropagation();
1040
- _this8.updateRule(index, 'format.decimalSep', e.target.value);
1041
- },
1042
- placeholder: '.',
1043
- maxLength: '1',
1044
- style: { width: '100%', padding: '4px' }
1045
- })
1046
- ),
1047
- _react2.default.createElement(
1048
- 'div',
1049
- { style: { marginBottom: '8px' } },
1050
- _react2.default.createElement(
1051
- 'label',
1052
- {
1053
- style: {
1054
- display: 'block',
1055
- marginBottom: '4px',
1056
- fontSize: '11px'
1057
- }
1058
- },
1059
- 'Decimal Places:'
1060
- ),
1061
- _react2.default.createElement('input', {
1062
- type: 'number',
1063
- min: '0',
1064
- max: '9',
1065
- value: rule.format && 'decimalPlaces' in rule.format ? rule.format.decimalPlaces : 2,
1066
- onChange: function onChange(e) {
1067
- e.stopPropagation();
1068
- var val = parseInt(e.target.value, 10);
1069
- _this8.updateRule(index, 'format.decimalPlaces', isNaN(val) ? 2 : val);
1070
- },
1071
- placeholder: '2',
1072
- style: { width: '100%', padding: '4px' }
1073
- })
1074
- ),
1075
- _react2.default.createElement(
1076
- 'div',
1077
- { style: { marginBottom: '8px' } },
1078
- _react2.default.createElement(
1079
- 'label',
1080
- {
1081
- style: {
1082
- display: 'block',
1083
- marginBottom: '4px',
1084
- fontSize: '11px'
1085
- }
1086
- },
1087
- 'Prefix (e.g., $, \u20AC):'
1088
- ),
1089
- _react2.default.createElement('input', {
1090
- type: 'text',
1091
- value: rule.format && 'prefix' in rule.format ? rule.format.prefix : '',
1092
- onChange: function onChange(e) {
1093
- e.stopPropagation();
1094
- _this8.updateRule(index, 'format.prefix', e.target.value);
1095
- },
1096
- placeholder: '',
1097
- style: { width: '100%', padding: '4px' }
1098
- })
1099
- ),
1100
- _react2.default.createElement(
1101
- 'div',
1102
- { style: { marginBottom: '8px' } },
1103
- _react2.default.createElement(
1104
- 'label',
1105
- {
1106
- style: {
1107
- display: 'block',
1108
- marginBottom: '4px',
1109
- fontSize: '11px'
1110
- }
1111
- },
1112
- 'Suffix (e.g., %, units):'
1113
- ),
1114
- _react2.default.createElement('input', {
1115
- type: 'text',
1116
- value: rule.format && 'suffix' in rule.format ? rule.format.suffix : '',
1117
- onChange: function onChange(e) {
1118
- e.stopPropagation();
1119
- _this8.updateRule(index, 'format.suffix', e.target.value);
1120
- },
1121
- placeholder: '',
1122
- style: { width: '100%', padding: '4px' }
1123
- })
1124
- ),
1125
- _react2.default.createElement(
1126
- 'div',
1127
- { style: { marginBottom: '8px' } },
1128
- _react2.default.createElement(
1129
- 'label',
1130
- {
1131
- style: {
1132
- display: 'flex',
1133
- alignItems: 'center',
1134
- marginBottom: '4px',
1135
- fontSize: '11px'
1136
- }
1137
- },
1138
- _react2.default.createElement('input', {
1139
- type: 'checkbox',
1140
- checked: rule.format && 'showOriginal' in rule.format ? rule.format.showOriginal : false,
1141
- onChange: function onChange(e) {
1142
- e.stopPropagation();
1143
- _this8.updateRule(index, 'format.showOriginal', e.target.checked);
1144
- },
1145
- style: { marginRight: '6px' }
1146
- }),
1147
- 'Show Original Value (no formatting)'
1148
- )
1149
- )
1150
- );
1151
- }),
990
+ 'Decimal Places:'
991
+ ),
992
+ _react2.default.createElement('input', {
993
+ type: 'number',
994
+ min: '0',
995
+ max: '9',
996
+ value: rule.format && 'decimalPlaces' in rule.format ? rule.format.decimalPlaces : 2,
997
+ onChange: function onChange(e) {
998
+ e.stopPropagation();
999
+ var val = parseInt(e.target.value, 10);
1000
+ updateRule(index, 'format.decimalPlaces', isNaN(val) ? 2 : val);
1001
+ },
1002
+ placeholder: '2',
1003
+ style: { width: '100%', padding: '4px' }
1004
+ })
1005
+ ),
1152
1006
  _react2.default.createElement(
1153
- 'button',
1154
- {
1155
- type: 'button',
1156
- className: 'pvtButton',
1157
- onClick: function onClick(e) {
1007
+ 'div',
1008
+ { style: { marginBottom: '8px' } },
1009
+ _react2.default.createElement(
1010
+ 'label',
1011
+ {
1012
+ style: {
1013
+ display: 'block',
1014
+ marginBottom: '4px',
1015
+ fontSize: '11px'
1016
+ }
1017
+ },
1018
+ 'Prefix (e.g., $, \u20AC):'
1019
+ ),
1020
+ _react2.default.createElement('input', {
1021
+ type: 'text',
1022
+ value: rule.format && 'prefix' in rule.format ? rule.format.prefix : '',
1023
+ onChange: function onChange(e) {
1158
1024
  e.stopPropagation();
1159
- _this8.addRule();
1025
+ updateRule(index, 'format.prefix', e.target.value);
1160
1026
  },
1161
- style: { width: '100%', marginTop: '10px' }
1162
- },
1163
- '+ Add Rule'
1027
+ placeholder: '',
1028
+ style: { width: '100%', padding: '4px' }
1029
+ })
1030
+ ),
1031
+ _react2.default.createElement(
1032
+ 'div',
1033
+ { style: { marginBottom: '8px' } },
1034
+ _react2.default.createElement(
1035
+ 'label',
1036
+ {
1037
+ style: {
1038
+ display: 'block',
1039
+ marginBottom: '4px',
1040
+ fontSize: '11px'
1041
+ }
1042
+ },
1043
+ 'Suffix (e.g., %, units):'
1044
+ ),
1045
+ _react2.default.createElement('input', {
1046
+ type: 'text',
1047
+ value: rule.format && 'suffix' in rule.format ? rule.format.suffix : '',
1048
+ onChange: function onChange(e) {
1049
+ e.stopPropagation();
1050
+ updateRule(index, 'format.suffix', e.target.value);
1051
+ },
1052
+ placeholder: '',
1053
+ style: { width: '100%', padding: '4px' }
1054
+ })
1055
+ ),
1056
+ _react2.default.createElement(
1057
+ 'div',
1058
+ { style: { marginBottom: '8px' } },
1059
+ _react2.default.createElement(
1060
+ 'label',
1061
+ {
1062
+ style: {
1063
+ display: 'flex',
1064
+ alignItems: 'center',
1065
+ marginBottom: '4px',
1066
+ fontSize: '11px'
1067
+ }
1068
+ },
1069
+ _react2.default.createElement('input', {
1070
+ type: 'checkbox',
1071
+ checked: rule.format && 'showOriginal' in rule.format ? rule.format.showOriginal : false,
1072
+ onChange: function onChange(e) {
1073
+ e.stopPropagation();
1074
+ updateRule(index, 'format.showOriginal', e.target.checked);
1075
+ },
1076
+ style: { marginRight: '6px' }
1077
+ }),
1078
+ 'Show Original Value (no formatting)'
1079
+ )
1164
1080
  )
1165
- )
1166
- );
1167
- }
1168
- }]);
1169
-
1170
- return CellFormattingUI;
1171
- }(_react2.default.Component);
1081
+ );
1082
+ }),
1083
+ _react2.default.createElement(
1084
+ 'button',
1085
+ {
1086
+ type: 'button',
1087
+ className: 'pvtButton',
1088
+ onClick: function onClick(e) {
1089
+ e.stopPropagation();
1090
+ addRule();
1091
+ },
1092
+ style: { width: '100%', marginTop: '10px' }
1093
+ },
1094
+ '+ Add Rule'
1095
+ )
1096
+ )
1097
+ );
1098
+ }
1172
1099
 
1173
1100
  CellFormattingUI.propTypes = {
1174
1101
  tableOptions: _propTypes2.default.object,
@@ -1183,643 +1110,830 @@ CellFormattingUI.defaultProps = {
1183
1110
  zIndex: 1
1184
1111
  };
1185
1112
 
1186
- var PivotTableUI = function (_React$PureComponent2) {
1187
- _inherits(PivotTableUI, _React$PureComponent2);
1113
+ // Used to namespace worker-side cached datasets when multiple PivotTableUI
1114
+ // instances share the same Web Worker.
1115
+ var pivotWorkerInstanceCounter = 0;
1116
+
1117
+ function PivotLoadingOverlay(props) {
1118
+ var progress = props.progress || {};
1119
+ var percent = progress.total > 0 ? Math.min(100, Math.round(progress.processed / progress.total * 100)) : null;
1120
+ var phaseLabels = {
1121
+ materialize: 'Reading data',
1122
+ aggregate: 'Aggregating data'
1123
+ };
1124
+ var phaseLabel = phaseLabels[progress.phase] || 'Processing';
1125
+ return _react2.default.createElement(
1126
+ 'div',
1127
+ { className: 'pvtLoadingOverlay', role: 'status' },
1128
+ _react2.default.createElement('div', { className: 'pvtLoadingSpinner', 'aria-hidden': 'true' }),
1129
+ _react2.default.createElement(
1130
+ 'div',
1131
+ { className: 'pvtLoadingText' },
1132
+ phaseLabel,
1133
+ '\u2026'
1134
+ ),
1135
+ percent !== null ? _react2.default.createElement(
1136
+ 'div',
1137
+ { className: 'pvtLoadingBarWrap' },
1138
+ _react2.default.createElement('div', { className: 'pvtLoadingBar', style: { width: percent + '%' } })
1139
+ ) : null,
1140
+ percent !== null ? _react2.default.createElement(
1141
+ 'div',
1142
+ { className: 'pvtLoadingPercent' },
1143
+ percent,
1144
+ '%'
1145
+ ) : null
1146
+ );
1147
+ }
1148
+
1149
+ PivotLoadingOverlay.propTypes = {
1150
+ progress: _propTypes2.default.object
1151
+ };
1188
1152
 
1189
- function PivotTableUI(props) {
1190
- _classCallCheck(this, PivotTableUI);
1153
+ function PivotTableUI(props) {
1154
+ var _useState9 = (0, _react.useState)([]),
1155
+ _useState10 = _slicedToArray(_useState9, 2),
1156
+ unusedOrder = _useState10[0],
1157
+ setUnusedOrder = _useState10[1];
1158
+
1159
+ var _useState11 = (0, _react.useState)({}),
1160
+ _useState12 = _slicedToArray(_useState11, 2),
1161
+ zIndices = _useState12[0],
1162
+ setZIndices = _useState12[1];
1163
+
1164
+ var _useState13 = (0, _react.useState)(1000),
1165
+ _useState14 = _slicedToArray(_useState13, 2),
1166
+ maxZIndex = _useState14[0],
1167
+ setMaxZIndex = _useState14[1];
1168
+
1169
+ var _useState15 = (0, _react.useState)(false),
1170
+ _useState16 = _slicedToArray(_useState15, 2),
1171
+ openDropdown = _useState16[0],
1172
+ setOpenDropdown = _useState16[1];
1173
+
1174
+ var _useState17 = (0, _react.useState)({}),
1175
+ _useState18 = _slicedToArray(_useState17, 2),
1176
+ attrValues = _useState18[0],
1177
+ setAttrValues = _useState18[1];
1178
+
1179
+ var _useState19 = (0, _react.useState)([]),
1180
+ _useState20 = _slicedToArray(_useState19, 2),
1181
+ materializedInput = _useState20[0],
1182
+ setMaterializedInput = _useState20[1];
1183
+
1184
+ // Tracks the last `data` prop that was materialized, mirroring the old
1185
+ // `this.state.data` check used to skip redundant work.
1186
+
1187
+
1188
+ var lastDataRef = (0, _react.useRef)(undefined);
1189
+
1190
+ // Worker orchestration state. `precomputed` holds the serialized pivot
1191
+ // state produced by the worker (or the synchronous fallback) together with
1192
+ // the exact compute signature it belongs to.
1193
+
1194
+ var _useState21 = (0, _react.useState)(function () {
1195
+ return (0, _pivotCompute.canComputeInWorker)(props, {
1196
+ enabled: props.enableWorker,
1197
+ threshold: props.workerThreshold
1198
+ });
1199
+ }),
1200
+ _useState22 = _slicedToArray(_useState21, 2),
1201
+ isProcessing = _useState22[0],
1202
+ setIsProcessing = _useState22[1];
1203
+
1204
+ var _useState23 = (0, _react.useState)(null),
1205
+ _useState24 = _slicedToArray(_useState23, 2),
1206
+ progress = _useState24[0],
1207
+ setProgress = _useState24[1];
1208
+
1209
+ var _useState25 = (0, _react.useState)(null),
1210
+ _useState26 = _slicedToArray(_useState25, 2),
1211
+ precomputed = _useState26[0],
1212
+ setPrecomputed = _useState26[1];
1213
+
1214
+ var _useState27 = (0, _react.useState)(null),
1215
+ _useState28 = _slicedToArray(_useState27, 2),
1216
+ computeError = _useState28[0],
1217
+ setComputeError = _useState28[1];
1218
+
1219
+ var precomputedRef = (0, _react.useRef)(null);
1220
+ var unmountedRef = (0, _react.useRef)(false);
1221
+ var activeRequestRef = (0, _react.useRef)(0);
1222
+ var dataVersionRef = (0, _react.useRef)(0);
1223
+ var versionedDataRef = (0, _react.useRef)(undefined);
1224
+ var instanceIdRef = (0, _react.useRef)(null);
1225
+ if (instanceIdRef.current === null) {
1226
+ instanceIdRef.current = 'pvt-' + pivotWorkerInstanceCounter++;
1227
+ }
1191
1228
 
1192
- var _this9 = _possibleConstructorReturn(this, (PivotTableUI.__proto__ || Object.getPrototypeOf(PivotTableUI)).call(this, props));
1229
+ var workerEligible = (0, _react.useMemo)(function () {
1230
+ return (0, _pivotCompute.canComputeInWorker)(props, {
1231
+ enabled: props.enableWorker,
1232
+ threshold: props.workerThreshold
1233
+ });
1234
+ }, [props.data, props.enableWorker, props.workerThreshold, props.derivedAttributes, props.aggregators]);
1193
1235
 
1194
- _this9.state = {
1195
- unusedOrder: [],
1196
- zIndices: {},
1197
- maxZIndex: 1000,
1198
- openDropdown: false,
1199
- attrValues: {},
1200
- materializedInput: []
1201
- };
1202
- return _this9;
1203
- }
1236
+ var computeSignature = (0, _react.useMemo)(function () {
1237
+ return (0, _pivotCompute.createComputeSignature)(props);
1238
+ }, [props.rows, props.cols, props.aggregatorName, props.aggregations, props.vals, props.valueFilter, props.sorters, props.rowOrder, props.colOrder, props.rowSorts, props.colSorts]);
1204
1239
 
1205
- _createClass(PivotTableUI, [{
1206
- key: 'componentDidMount',
1207
- value: function componentDidMount() {
1208
- this.materializeInput(this.props.data);
1240
+ var materializeInput = function materializeInput(nextData) {
1241
+ if (lastDataRef.current === nextData) {
1242
+ return;
1209
1243
  }
1210
- }, {
1211
- key: 'componentDidUpdate',
1212
- value: function componentDidUpdate() {
1213
- this.materializeInput(this.props.data);
1244
+ lastDataRef.current = nextData;
1245
+
1246
+ var materialized = (0, _pivotCompute.materializeData)(nextData, props.derivedAttributes);
1247
+ setAttrValues(materialized.attrValues);
1248
+ setMaterializedInput(materialized.materializedInput);
1249
+ };
1250
+
1251
+ (0, _react.useEffect)(function () {
1252
+ if (workerEligible) {
1253
+ // The worker (or its synchronous fallback) computes attrValues and the
1254
+ // materialized input; scanning 200k+ records here would block the
1255
+ // main thread again.
1256
+ return undefined;
1214
1257
  }
1215
- }, {
1216
- key: 'materializeInput',
1217
- value: function materializeInput(nextData) {
1218
- if (this.state.data === nextData) {
1219
- return;
1220
- }
1221
- var newState = {
1222
- data: nextData,
1223
- attrValues: {},
1224
- materializedInput: []
1258
+ materializeInput(props.data);
1259
+ return undefined;
1260
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1261
+ }, [props.data, workerEligible]);
1262
+
1263
+ (0, _react.useEffect)(function () {
1264
+ return function () {
1265
+ unmountedRef.current = true;
1266
+ };
1267
+ }, []);
1268
+
1269
+ // Snapshot of the last consistently rendered pivot view: the props that
1270
+ // match the currently applied precomputed result. While a new computation
1271
+ // runs, this snapshot is rendered underneath the loading overlay so the
1272
+ // user still sees the previous table (dimmed) instead of a blank area.
1273
+ var lastGoodViewRef = (0, _react.useRef)(null);
1274
+
1275
+ (0, _react.useEffect)(function () {
1276
+ if (!isProcessing && !computeError) {
1277
+ lastGoodViewRef.current = {
1278
+ props: props,
1279
+ materializedInput: materializedInput,
1280
+ precomputed: workerEligible && precomputed ? precomputed : null
1225
1281
  };
1226
- var recordsProcessed = 0;
1227
- _Utilities.PivotData.forEachRecord(newState.data, this.props.derivedAttributes, function (record) {
1228
- newState.materializedInput.push(record);
1229
- var _iteratorNormalCompletion = true;
1230
- var _didIteratorError = false;
1231
- var _iteratorError = undefined;
1232
-
1233
- try {
1234
- for (var _iterator = Object.keys(record)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
1235
- var attr = _step.value;
1236
-
1237
- if (!(attr in newState.attrValues)) {
1238
- newState.attrValues[attr] = {};
1239
- if (recordsProcessed > 0) {
1240
- newState.attrValues[attr].null = recordsProcessed;
1241
- }
1242
- }
1243
- }
1244
- } catch (err) {
1245
- _didIteratorError = true;
1246
- _iteratorError = err;
1247
- } finally {
1248
- try {
1249
- if (!_iteratorNormalCompletion && _iterator.return) {
1250
- _iterator.return();
1251
- }
1252
- } finally {
1253
- if (_didIteratorError) {
1254
- throw _iteratorError;
1255
- }
1256
- }
1257
- }
1258
-
1259
- for (var _attr in newState.attrValues) {
1260
- var value = _attr in record ? record[_attr] : 'null';
1261
- if (!(value in newState.attrValues[_attr])) {
1262
- newState.attrValues[_attr][value] = 0;
1263
- }
1264
- newState.attrValues[_attr][value]++;
1265
- }
1266
- recordsProcessed++;
1267
- });
1268
- this.setState(newState);
1269
1282
  }
1270
- }, {
1271
- key: 'sendPropUpdate',
1272
- value: function sendPropUpdate(command) {
1273
- this.props.onChange((0, _immutabilityHelper2.default)(this.props, command));
1283
+ });
1284
+
1285
+ // Schedule an off-main-thread computation whenever the data or the pivot
1286
+ // configuration changes. Rapid consecutive changes are debounced and
1287
+ // stale responses are discarded.
1288
+ (0, _react.useEffect)(function () {
1289
+ // Invalidate any in-flight request from a previous effect run so its
1290
+ // result is never applied to the new configuration or dataset.
1291
+ activeRequestRef.current += 1;
1292
+
1293
+ if (!workerEligible) {
1294
+ setIsProcessing(false);
1295
+ return undefined;
1274
1296
  }
1275
- }, {
1276
- key: 'propUpdater',
1277
- value: function propUpdater(key) {
1278
- var _this10 = this;
1279
-
1280
- return function (value) {
1281
- return _this10.sendPropUpdate(_defineProperty({}, key, { $set: value }));
1282
- };
1297
+ if (versionedDataRef.current !== props.data) {
1298
+ versionedDataRef.current = props.data;
1299
+ dataVersionRef.current += 1;
1283
1300
  }
1284
- }, {
1285
- key: 'hasExternalAggregations',
1286
- value: function hasExternalAggregations() {
1287
- return Array.isArray(this.props.aggregations);
1301
+ var signature = computeSignature + '#' + dataVersionRef.current;
1302
+ if (precomputedRef.current && precomputedRef.current.signature === signature) {
1303
+ setIsProcessing(false);
1304
+ return undefined;
1288
1305
  }
1289
- }, {
1290
- key: 'getCurrentAggregations',
1291
- value: function getCurrentAggregations() {
1292
- var _this11 = this;
1293
-
1294
- var fallbackAggregator = this.props.aggregatorName || Object.keys(this.props.aggregators)[0];
1295
- var fallbackVals = Array.isArray(this.props.vals) ? this.props.vals.slice() : [];
1296
- var aggregationsProvided = this.hasExternalAggregations();
1297
- var defaultAggregations = [{
1298
- key: 'agg-default',
1299
- aggregatorName: fallbackAggregator,
1300
- vals: fallbackVals
1301
- }];
1302
- var source = aggregationsProvided ? this.props.aggregations : defaultAggregations;
1303
- return source.map(function (agg, idx) {
1304
- var aggregatorName = agg.aggregatorName || fallbackAggregator;
1305
- var baseVals = Array.isArray(agg.vals) ? agg.vals.slice() : aggregationsProvided ? [] : fallbackVals;
1306
- return {
1307
- key: agg.key || 'agg-' + idx,
1308
- aggregatorName: aggregatorName,
1309
- vals: _this11.defaultValsForAggregator(aggregatorName, baseVals)
1310
- };
1306
+ (0, _PivotDataWorker.configureWorkerUrl)(props.workerUrl || _PivotDataWorker.DEFAULT_WORKER_URL);
1307
+ setIsProcessing(true);
1308
+ setComputeError(null);
1309
+ setProgress({ processed: 0, total: props.data.length, phase: 'queued' });
1310
+ var timer = setTimeout(function () {
1311
+ var requestId = activeRequestRef.current;
1312
+ var dataKey = instanceIdRef.current + '-data-' + dataVersionRef.current;
1313
+ (0, _PivotDataWorker.computePivotWithFallback)(props.data, dataKey, (0, _pivotCompute.createWorkerConfig)(props), function (processed, total, phase) {
1314
+ if (unmountedRef.current || requestId !== activeRequestRef.current) {
1315
+ return;
1316
+ }
1317
+ setProgress({ processed: processed, total: total, phase: phase });
1318
+ }).then(function (result) {
1319
+ if (unmountedRef.current || requestId !== activeRequestRef.current) {
1320
+ return;
1321
+ }
1322
+ precomputedRef.current = { signature: signature, data: result };
1323
+ setPrecomputed(precomputedRef.current);
1324
+ setAttrValues(result.attrValues);
1325
+ setMaterializedInput(result.materializedInput);
1326
+ setComputeError(null);
1327
+ setIsProcessing(false);
1328
+ }).catch(function (error) {
1329
+ if (unmountedRef.current || requestId !== activeRequestRef.current) {
1330
+ return;
1331
+ }
1332
+ setComputeError(error && error.message || 'Failed to compute pivot table');
1333
+ setIsProcessing(false);
1311
1334
  });
1312
- }
1313
- }, {
1314
- key: 'numInputsForAggregator',
1315
- value: function numInputsForAggregator(name) {
1316
- if (!name || !(name in this.props.aggregators)) {
1317
- return 0;
1318
- }
1319
- var instance = this.props.aggregators[name]([])();
1320
- return instance.numInputs || 0;
1321
- }
1322
- }, {
1323
- key: 'getAttributeOptions',
1324
- value: function getAttributeOptions() {
1325
- var _this12 = this;
1335
+ }, _pivotCompute.WORKER_DEBOUNCE_MS);
1336
+ return function () {
1337
+ clearTimeout(timer);
1338
+ };
1339
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1340
+ }, [props.data, workerEligible, computeSignature]);
1326
1341
 
1327
- return Object.keys(this.state.attrValues).filter(function (e) {
1328
- return !_this12.props.hiddenAttributes.includes(e) && !_this12.props.hiddenFromAggregators.includes(e);
1329
- });
1330
- }
1331
- }, {
1332
- key: 'defaultValsForAggregator',
1333
- value: function defaultValsForAggregator(name) {
1334
- var currentVals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
1335
-
1336
- var required = this.numInputsForAggregator(name);
1337
- if (required === 0) {
1338
- return [];
1339
- }
1340
- var options = this.getAttributeOptions();
1341
- var next = currentVals.slice(0, required);
1342
- for (var i = 0; i < required; i++) {
1343
- if (!next[i]) {
1344
- next[i] = options[i % options.length] || null;
1345
- }
1346
- }
1347
- return next;
1342
+ var sendPropUpdate = function sendPropUpdate(command) {
1343
+ props.onChange((0, _immutabilityHelper2.default)(props, command));
1344
+ };
1345
+
1346
+ var propUpdater = function propUpdater(key) {
1347
+ return function (value) {
1348
+ return sendPropUpdate(_defineProperty({}, key, { $set: value }));
1349
+ };
1350
+ };
1351
+
1352
+ var hasExternalAggregations = function hasExternalAggregations() {
1353
+ return Array.isArray(props.aggregations);
1354
+ };
1355
+
1356
+ var numInputsForAggregator = function numInputsForAggregator(name) {
1357
+ if (!name || !(name in props.aggregators)) {
1358
+ return 0;
1348
1359
  }
1349
- }, {
1350
- key: 'generateAggregationKey',
1351
- value: function generateAggregationKey() {
1352
- var timestamp = Date.now().toString(AGG_KEY_RADIX);
1353
- var randomPart = Math.random().toString(AGG_KEY_RADIX).slice(AGG_KEY_OFFSET, AGG_KEY_OFFSET + AGG_KEY_LENGTH);
1354
- return 'agg-' + timestamp + '-' + randomPart;
1360
+ var instance = props.aggregators[name]([])();
1361
+ return instance.numInputs || 0;
1362
+ };
1363
+
1364
+ var getAttributeOptions = function getAttributeOptions() {
1365
+ return Object.keys(attrValues).filter(function (e) {
1366
+ return !props.hiddenAttributes.includes(e) && !props.hiddenFromAggregators.includes(e);
1367
+ });
1368
+ };
1369
+
1370
+ var defaultValsForAggregator = function defaultValsForAggregator(name) {
1371
+ var currentVals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
1372
+
1373
+ var required = numInputsForAggregator(name);
1374
+ if (required === 0) {
1375
+ return [];
1355
1376
  }
1356
- }, {
1357
- key: 'updateAggregations',
1358
- value: function updateAggregations(nextAggregations) {
1359
- var _this13 = this;
1360
-
1361
- if (!nextAggregations.length) {
1362
- return;
1377
+ var options = getAttributeOptions();
1378
+ var next = currentVals.slice(0, required);
1379
+ for (var i = 0; i < required; i++) {
1380
+ if (!next[i]) {
1381
+ next[i] = options[i % options.length] || null;
1363
1382
  }
1364
- var fallbackAggregator = this.props.aggregatorName || Object.keys(this.props.aggregators)[0];
1365
- var sanitized = nextAggregations.map(function (agg, idx) {
1366
- var aggregatorName = agg.aggregatorName || fallbackAggregator;
1367
- return {
1368
- key: agg.key || 'agg-' + idx,
1369
- aggregatorName: aggregatorName,
1370
- vals: _this13.defaultValsForAggregator(aggregatorName, Array.isArray(agg.vals) ? agg.vals.slice() : [])
1371
- };
1372
- });
1373
- var primary = sanitized[0];
1374
- var command = {
1375
- aggregations: { $set: sanitized }
1383
+ }
1384
+ return next;
1385
+ };
1386
+
1387
+ var getCurrentAggregations = function getCurrentAggregations() {
1388
+ var fallbackAggregator = props.aggregatorName || Object.keys(props.aggregators)[0];
1389
+ var fallbackVals = Array.isArray(props.vals) ? props.vals.slice() : [];
1390
+ var aggregationsProvided = hasExternalAggregations();
1391
+ var defaultAggregations = [{
1392
+ key: 'agg-default',
1393
+ aggregatorName: fallbackAggregator,
1394
+ vals: fallbackVals
1395
+ }];
1396
+ var source = aggregationsProvided ? props.aggregations : defaultAggregations;
1397
+ return source.map(function (agg, idx) {
1398
+ var aggregatorName = agg.aggregatorName || fallbackAggregator;
1399
+ var baseVals = Array.isArray(agg.vals) ? agg.vals.slice() : aggregationsProvided ? [] : fallbackVals;
1400
+ return {
1401
+ key: agg.key || 'agg-' + idx,
1402
+ aggregatorName: aggregatorName,
1403
+ vals: defaultValsForAggregator(aggregatorName, baseVals)
1376
1404
  };
1377
- if (!this.hasExternalAggregations()) {
1378
- command.aggregatorName = { $set: primary.aggregatorName };
1379
- command.vals = { $set: primary.vals };
1380
- }
1381
- this.sendPropUpdate(command);
1405
+ });
1406
+ };
1407
+
1408
+ var generateAggregationKey = function generateAggregationKey() {
1409
+ var timestamp = Date.now().toString(AGG_KEY_RADIX);
1410
+ var randomPart = Math.random().toString(AGG_KEY_RADIX).slice(AGG_KEY_OFFSET, AGG_KEY_OFFSET + AGG_KEY_LENGTH);
1411
+ return 'agg-' + timestamp + '-' + randomPart;
1412
+ };
1413
+
1414
+ var updateAggregations = function updateAggregations(nextAggregations) {
1415
+ if (!nextAggregations.length) {
1416
+ return;
1382
1417
  }
1383
- }, {
1384
- key: 'setAggregationAggregator',
1385
- value: function setAggregationAggregator(index, aggregatorName) {
1386
- var aggregations = this.getCurrentAggregations();
1387
- var next = Object.assign({}, aggregations[index], {
1388
- aggregatorName: aggregatorName
1389
- });
1390
- next.vals = this.defaultValsForAggregator(aggregatorName, next.vals);
1391
- aggregations[index] = next;
1392
- this.updateAggregations(aggregations);
1418
+ var fallbackAggregator = props.aggregatorName || Object.keys(props.aggregators)[0];
1419
+ var sanitized = nextAggregations.map(function (agg, idx) {
1420
+ var aggregatorName = agg.aggregatorName || fallbackAggregator;
1421
+ return {
1422
+ key: agg.key || 'agg-' + idx,
1423
+ aggregatorName: aggregatorName,
1424
+ vals: defaultValsForAggregator(aggregatorName, Array.isArray(agg.vals) ? agg.vals.slice() : [])
1425
+ };
1426
+ });
1427
+ var primary = sanitized[0];
1428
+ var command = {
1429
+ aggregations: { $set: sanitized }
1430
+ };
1431
+ if (!hasExternalAggregations()) {
1432
+ command.aggregatorName = { $set: primary.aggregatorName };
1433
+ command.vals = { $set: primary.vals };
1393
1434
  }
1394
- }, {
1395
- key: 'setAggregationVal',
1396
- value: function setAggregationVal(index, valIndex, value) {
1397
- var aggregations = this.getCurrentAggregations();
1398
- var next = Object.assign({}, aggregations[index]);
1399
- var vals = Array.isArray(next.vals) ? next.vals.slice() : [];
1400
- vals[valIndex] = value;
1401
- next.vals = vals;
1402
- aggregations[index] = next;
1403
- this.updateAggregations(aggregations);
1435
+ sendPropUpdate(command);
1436
+ };
1437
+
1438
+ var setAggregationAggregator = function setAggregationAggregator(index, aggregatorName) {
1439
+ var aggregations = getCurrentAggregations();
1440
+ var next = Object.assign({}, aggregations[index], {
1441
+ aggregatorName: aggregatorName
1442
+ });
1443
+ next.vals = defaultValsForAggregator(aggregatorName, next.vals);
1444
+ aggregations[index] = next;
1445
+ updateAggregations(aggregations);
1446
+ };
1447
+
1448
+ var setAggregationVal = function setAggregationVal(index, valIndex, value) {
1449
+ var aggregations = getCurrentAggregations();
1450
+ var next = Object.assign({}, aggregations[index]);
1451
+ var vals = Array.isArray(next.vals) ? next.vals.slice() : [];
1452
+ vals[valIndex] = value;
1453
+ next.vals = vals;
1454
+ aggregations[index] = next;
1455
+ updateAggregations(aggregations);
1456
+ };
1457
+
1458
+ var setAggregationDisplayMode = function setAggregationDisplayMode(mode) {
1459
+ var allowedModes = ['row', 'column'];
1460
+ var normalizedMode = allowedModes.includes(mode) ? mode : 'row';
1461
+ var tableOptions = Object.assign({}, props.tableOptions || {}, {
1462
+ aggregationDisplayMode: normalizedMode
1463
+ });
1464
+ sendPropUpdate({
1465
+ tableOptions: { $set: tableOptions }
1466
+ });
1467
+ };
1468
+
1469
+ var addAggregationRow = function addAggregationRow() {
1470
+ var aggregations = getCurrentAggregations();
1471
+ var defaultAggregator = props.aggregatorName || Object.keys(props.aggregators)[0];
1472
+ var key = generateAggregationKey();
1473
+ aggregations.push({
1474
+ key: key,
1475
+ aggregatorName: defaultAggregator,
1476
+ vals: defaultValsForAggregator(defaultAggregator)
1477
+ });
1478
+ updateAggregations(aggregations);
1479
+ };
1480
+
1481
+ var removeAggregationRow = function removeAggregationRow(index) {
1482
+ var aggregations = getCurrentAggregations();
1483
+ if (aggregations.length === 1) {
1484
+ return;
1404
1485
  }
1405
- }, {
1406
- key: 'setAggregationDisplayMode',
1407
- value: function setAggregationDisplayMode(mode) {
1408
- var allowedModes = ['row', 'column'];
1409
- var normalizedMode = allowedModes.includes(mode) ? mode : 'row';
1410
- var tableOptions = Object.assign({}, this.props.tableOptions || {}, {
1411
- aggregationDisplayMode: normalizedMode
1412
- });
1413
- this.sendPropUpdate({
1414
- tableOptions: { $set: tableOptions }
1486
+ aggregations.splice(index, 1);
1487
+ updateAggregations(aggregations);
1488
+ };
1489
+
1490
+ var setValuesInFilter = function setValuesInFilter(attribute, values) {
1491
+ sendPropUpdate({
1492
+ valueFilter: _defineProperty({}, attribute, {
1493
+ $set: values.reduce(function (r, v) {
1494
+ r[v] = true;
1495
+ return r;
1496
+ }, {})
1497
+ })
1498
+ });
1499
+ };
1500
+
1501
+ var addValuesToFilter = function addValuesToFilter(attribute, values) {
1502
+ if (attribute in props.valueFilter) {
1503
+ sendPropUpdate({
1504
+ valueFilter: _defineProperty({}, attribute, values.reduce(function (r, v) {
1505
+ r[v] = { $set: true };
1506
+ return r;
1507
+ }, {}))
1415
1508
  });
1509
+ } else {
1510
+ setValuesInFilter(attribute, values);
1416
1511
  }
1417
- }, {
1418
- key: 'addAggregationRow',
1419
- value: function addAggregationRow() {
1420
- var aggregations = this.getCurrentAggregations();
1421
- var defaultAggregator = this.props.aggregatorName || Object.keys(this.props.aggregators)[0];
1422
- var key = this.generateAggregationKey();
1423
- aggregations.push({
1424
- key: key,
1425
- aggregatorName: defaultAggregator,
1426
- vals: this.defaultValsForAggregator(defaultAggregator)
1512
+ };
1513
+
1514
+ var removeValuesFromFilter = function removeValuesFromFilter(attribute, values) {
1515
+ sendPropUpdate({
1516
+ valueFilter: _defineProperty({}, attribute, { $unset: values })
1517
+ });
1518
+ };
1519
+
1520
+ var moveFilterBoxToTop = function moveFilterBoxToTop(attribute) {
1521
+ setMaxZIndex(function (prevMax) {
1522
+ var nextMax = prevMax + 1;
1523
+ setZIndices(function (prevZ) {
1524
+ return Object.assign({}, prevZ, _defineProperty({}, attribute, nextMax));
1427
1525
  });
1428
- this.updateAggregations(aggregations);
1526
+ return nextMax;
1527
+ });
1528
+ };
1529
+
1530
+ var handleColSort = function handleColSort(attr, sortDir) {
1531
+ // Only allow sorting 1 column at a time - clear all other column sorts
1532
+ var colSorts = {};
1533
+ if (sortDir !== null) {
1534
+ colSorts[attr] = sortDir;
1429
1535
  }
1430
- }, {
1431
- key: 'removeAggregationRow',
1432
- value: function removeAggregationRow(index) {
1433
- var aggregations = this.getCurrentAggregations();
1434
- if (aggregations.length === 1) {
1435
- return;
1436
- }
1437
- aggregations.splice(index, 1);
1438
- this.updateAggregations(aggregations);
1536
+ sendPropUpdate({ colSorts: { $set: colSorts } });
1537
+ };
1538
+
1539
+ var handleRowSort = function handleRowSort(attr, sortDir) {
1540
+ // Only allow sorting 1 row at a time - clear all other row sorts
1541
+ var rowSorts = {};
1542
+ if (sortDir !== null) {
1543
+ rowSorts[attr] = sortDir;
1439
1544
  }
1440
- }, {
1441
- key: 'setValuesInFilter',
1442
- value: function setValuesInFilter(attribute, values) {
1443
- this.sendPropUpdate({
1444
- valueFilter: _defineProperty({}, attribute, {
1445
- $set: values.reduce(function (r, v) {
1446
- r[v] = true;
1447
- return r;
1448
- }, {})
1449
- })
1450
- });
1545
+ sendPropUpdate({ rowSorts: { $set: rowSorts } });
1546
+ };
1547
+
1548
+ var isOpen = function isOpen(dropdown) {
1549
+ return openDropdown === dropdown;
1550
+ };
1551
+
1552
+ /*
1553
+ * Drag-and-drop updates must stay consistent even though SortableJS
1554
+ * manipulates the DOM directly (the dragged node can transiently exist in
1555
+ * two containers while React reconciles). Two guards are applied:
1556
+ * 1. duplicate entries delivered by onChange are dropped (first wins), and
1557
+ * 2. rows/cols are kept mutually exclusive: dragging a field into one
1558
+ * axis removes it from the other one ($apply keeps whatever order the
1559
+ * axis currently has, even if another update landed in the same batch).
1560
+ */
1561
+ var setAxisFromDrag = function setAxisFromDrag(listKey, order) {
1562
+ var uniqueOrder = Array.from(new Set(order));
1563
+ var otherKey = listKey === 'rows' ? 'cols' : 'rows';
1564
+ var otherList = Array.from(new Set(props[otherKey] || []));
1565
+ var overlap = uniqueOrder.filter(function (name) {
1566
+ return otherList.includes(name);
1567
+ });
1568
+ var command = _defineProperty({}, listKey, { $set: uniqueOrder });
1569
+ if (overlap.length > 0) {
1570
+ command[otherKey] = {
1571
+ $apply: function $apply(current) {
1572
+ return Array.from(new Set(current || [])).filter(function (name) {
1573
+ return !overlap.includes(name);
1574
+ });
1575
+ }
1576
+ };
1451
1577
  }
1452
- }, {
1453
- key: 'addValuesToFilter',
1454
- value: function addValuesToFilter(attribute, values) {
1455
- if (attribute in this.props.valueFilter) {
1456
- this.sendPropUpdate({
1457
- valueFilter: _defineProperty({}, attribute, values.reduce(function (r, v) {
1458
- r[v] = { $set: true };
1459
- return r;
1460
- }, {}))
1578
+ sendPropUpdate(command);
1579
+ };
1580
+
1581
+ var makeDnDCell = function makeDnDCell(items, _onChange, classes) {
1582
+ return _react2.default.createElement(
1583
+ _reactSortablejs2.default,
1584
+ {
1585
+ options: {
1586
+ group: 'shared',
1587
+ ghostClass: 'pvtPlaceholder',
1588
+ filter: '.pvtFilterBox',
1589
+ preventOnFilter: false
1590
+ },
1591
+ tag: 'td',
1592
+ className: classes,
1593
+ onChange: function onChange(order) {
1594
+ return _onChange(Array.from(new Set(order)));
1595
+ }
1596
+ },
1597
+ items.map(function (x) {
1598
+ return _react2.default.createElement(DraggableAttribute, {
1599
+ name: x,
1600
+ key: x,
1601
+ attrValues: attrValues[x],
1602
+ valueFilter: props.valueFilter[x] || {},
1603
+ sorter: (0, _Utilities.getSort)(props.sorters, x, attrValues[x]),
1604
+ menuLimit: props.menuLimit,
1605
+ setValuesInFilter: setValuesInFilter,
1606
+ addValuesToFilter: addValuesToFilter,
1607
+ moveFilterBoxToTop: moveFilterBoxToTop,
1608
+ removeValuesFromFilter: removeValuesFromFilter,
1609
+ zIndex: zIndices[x] || maxZIndex
1461
1610
  });
1462
- } else {
1463
- this.setValuesInFilter(attribute, values);
1464
- }
1465
- }
1466
- }, {
1467
- key: 'removeValuesFromFilter',
1468
- value: function removeValuesFromFilter(attribute, values) {
1469
- this.sendPropUpdate({
1470
- valueFilter: _defineProperty({}, attribute, { $unset: values })
1471
- });
1472
- }
1473
- }, {
1474
- key: 'moveFilterBoxToTop',
1475
- value: function moveFilterBoxToTop(attribute) {
1476
- this.setState((0, _immutabilityHelper2.default)(this.state, {
1477
- maxZIndex: { $set: this.state.maxZIndex + 1 },
1478
- zIndices: _defineProperty({}, attribute, { $set: this.state.maxZIndex + 1 })
1479
- }));
1480
- }
1481
- }, {
1482
- key: 'handleColSort',
1483
- value: function handleColSort(attr, sortDir) {
1484
- // Only allow sorting 1 column at a time - clear all other column sorts
1485
- var colSorts = {};
1486
- if (sortDir !== null) {
1487
- colSorts[attr] = sortDir;
1488
- }
1489
- this.sendPropUpdate({ colSorts: { $set: colSorts } });
1490
- }
1491
- }, {
1492
- key: 'handleRowSort',
1493
- value: function handleRowSort(attr, sortDir) {
1494
- // Only allow sorting 1 row at a time - clear all other row sorts
1495
- var rowSorts = {};
1496
- if (sortDir !== null) {
1497
- rowSorts[attr] = sortDir;
1498
- }
1499
- this.sendPropUpdate({ rowSorts: { $set: rowSorts } });
1500
- }
1501
- }, {
1502
- key: 'isOpen',
1503
- value: function isOpen(dropdown) {
1504
- return this.state.openDropdown === dropdown;
1505
- }
1506
- }, {
1507
- key: 'makeDnDCell',
1508
- value: function makeDnDCell(items, onChange, classes) {
1509
- var _this14 = this;
1510
-
1611
+ })
1612
+ );
1613
+ };
1614
+
1615
+ var aggregations = getCurrentAggregations();
1616
+ var primaryAggregation = aggregations[0];
1617
+ var tableOptions = props.tableOptions || {};
1618
+ var aggregationDisplayMode = tableOptions.aggregationDisplayMode === 'column' ? 'column' : 'row';
1619
+ var aggregatorCellOutlet = primaryAggregation && props.aggregators[primaryAggregation.aggregatorName] && props.aggregators[primaryAggregation.aggregatorName]([])().outlet;
1620
+
1621
+ var rendererName = props.rendererName in props.renderers ? props.rendererName : Object.keys(props.renderers)[0];
1622
+
1623
+ var rendererCell = _react2.default.createElement(
1624
+ 'td',
1625
+ { className: 'pvtRenderers' },
1626
+ _react2.default.createElement(Dropdown, {
1627
+ current: rendererName,
1628
+ values: Object.keys(props.renderers),
1629
+ open: isOpen('renderer'),
1630
+ zIndex: isOpen('renderer') ? maxZIndex + 1 : 1,
1631
+ toggle: function toggle() {
1632
+ return setOpenDropdown(isOpen('renderer') ? false : 'renderer');
1633
+ },
1634
+ setValue: propUpdater('rendererName')
1635
+ })
1636
+ );
1637
+
1638
+ var sortIcons = {
1639
+ key_a_to_z: {
1640
+ rowSymbol: '↕',
1641
+ colSymbol: '↔',
1642
+ next: 'value_a_to_z'
1643
+ },
1644
+ value_a_to_z: {
1645
+ rowSymbol: '↓',
1646
+ colSymbol: '→',
1647
+ next: 'value_z_to_a'
1648
+ },
1649
+ value_z_to_a: { rowSymbol: '↑', colSymbol: '←', next: 'key_a_to_z' }
1650
+ };
1651
+
1652
+ var attributeOptions = getAttributeOptions();
1653
+ var aggregatorCell = _react2.default.createElement(
1654
+ 'td',
1655
+ { className: 'pvtVals' },
1656
+ aggregations.map(function (agg, idx) {
1657
+ var aggDropdownKey = 'aggregation-' + agg.key + '-agg';
1658
+ var numValsAllowed = numInputsForAggregator(agg.aggregatorName);
1659
+ var vals = defaultValsForAggregator(agg.aggregatorName, agg.vals);
1511
1660
  return _react2.default.createElement(
1512
- _reactSortablejs2.default,
1513
- {
1514
- options: {
1515
- group: 'shared',
1516
- ghostClass: 'pvtPlaceholder',
1517
- filter: '.pvtFilterBox',
1518
- preventOnFilter: false
1519
- },
1520
- tag: 'td',
1521
- className: classes,
1522
- onChange: onChange
1523
- },
1524
- items.map(function (x) {
1525
- return _react2.default.createElement(DraggableAttribute, {
1526
- name: x,
1527
- key: x,
1528
- attrValues: _this14.state.attrValues[x],
1529
- valueFilter: _this14.props.valueFilter[x] || {},
1530
- sorter: (0, _Utilities.getSort)(_this14.props.sorters, x, _this14.state.attrValues[x]),
1531
- menuLimit: _this14.props.menuLimit,
1532
- setValuesInFilter: _this14.setValuesInFilter.bind(_this14),
1533
- addValuesToFilter: _this14.addValuesToFilter.bind(_this14),
1534
- moveFilterBoxToTop: _this14.moveFilterBoxToTop.bind(_this14),
1535
- removeValuesFromFilter: _this14.removeValuesFromFilter.bind(_this14),
1536
- zIndex: _this14.state.zIndices[x] || _this14.state.maxZIndex
1537
- });
1538
- })
1539
- );
1540
- }
1541
- }, {
1542
- key: 'render',
1543
- value: function render() {
1544
- var _this15 = this;
1545
-
1546
- var aggregations = this.getCurrentAggregations();
1547
- var primaryAggregation = aggregations[0];
1548
- var tableOptions = this.props.tableOptions || {};
1549
- var aggregationDisplayMode = tableOptions.aggregationDisplayMode === 'column' ? 'column' : 'row';
1550
- var aggregatorCellOutlet = primaryAggregation && this.props.aggregators[primaryAggregation.aggregatorName] && this.props.aggregators[primaryAggregation.aggregatorName]([])().outlet;
1551
-
1552
- var rendererName = this.props.rendererName in this.props.renderers ? this.props.rendererName : Object.keys(this.props.renderers)[0];
1553
-
1554
- var rendererCell = _react2.default.createElement(
1555
- 'td',
1556
- { className: 'pvtRenderers' },
1661
+ 'div',
1662
+ { className: 'pvtAggregatorRow', key: agg.key || idx },
1557
1663
  _react2.default.createElement(Dropdown, {
1558
- current: rendererName,
1559
- values: Object.keys(this.props.renderers),
1560
- open: this.isOpen('renderer'),
1561
- zIndex: this.isOpen('renderer') ? this.state.maxZIndex + 1 : 1,
1664
+ current: agg.aggregatorName,
1665
+ values: Object.keys(props.aggregators),
1666
+ open: isOpen(aggDropdownKey),
1667
+ zIndex: isOpen(aggDropdownKey) ? maxZIndex + 1 : 1,
1562
1668
  toggle: function toggle() {
1563
- return _this15.setState({
1564
- openDropdown: _this15.isOpen('renderer') ? false : 'renderer'
1565
- });
1669
+ return setOpenDropdown(isOpen(aggDropdownKey) ? false : aggDropdownKey);
1566
1670
  },
1567
- setValue: this.propUpdater('rendererName')
1568
- })
1569
- );
1570
-
1571
- var sortIcons = {
1572
- key_a_to_z: {
1573
- rowSymbol: '↕',
1574
- colSymbol: '↔',
1575
- next: 'value_a_to_z'
1576
- },
1577
- value_a_to_z: {
1578
- rowSymbol: '↓',
1579
- colSymbol: '→',
1580
- next: 'value_z_to_a'
1581
- },
1582
- value_z_to_a: { rowSymbol: '↑', colSymbol: '←', next: 'key_a_to_z' }
1583
- };
1584
-
1585
- var attributeOptions = this.getAttributeOptions();
1586
- var aggregatorCell = _react2.default.createElement(
1587
- 'td',
1588
- { className: 'pvtVals' },
1589
- aggregations.map(function (agg, idx) {
1590
- var aggDropdownKey = 'aggregation-' + agg.key + '-agg';
1591
- var numValsAllowed = _this15.numInputsForAggregator(agg.aggregatorName);
1592
- var vals = _this15.defaultValsForAggregator(agg.aggregatorName, agg.vals);
1593
- return _react2.default.createElement(
1594
- 'div',
1595
- { className: 'pvtAggregatorRow', key: agg.key || idx },
1596
- _react2.default.createElement(Dropdown, {
1597
- current: agg.aggregatorName,
1598
- values: Object.keys(_this15.props.aggregators),
1599
- open: _this15.isOpen(aggDropdownKey),
1600
- zIndex: _this15.isOpen(aggDropdownKey) ? _this15.state.maxZIndex + 1 : 1,
1671
+ setValue: function setValue(value) {
1672
+ return setAggregationAggregator(idx, value);
1673
+ }
1674
+ }),
1675
+ numValsAllowed > 0 && _react2.default.createElement(
1676
+ 'div',
1677
+ { className: 'pvtAggregatorVals' },
1678
+ new Array(numValsAllowed).fill().map(function (n, i) {
1679
+ var valDropdownKey = 'aggregation-' + agg.key + '-val-' + i;
1680
+ return _react2.default.createElement(Dropdown, {
1681
+ key: 'agg-val-' + agg.key + '-' + i,
1682
+ current: vals[i],
1683
+ values: attributeOptions,
1684
+ open: isOpen(valDropdownKey),
1685
+ zIndex: isOpen(valDropdownKey) ? maxZIndex + 1 : 1,
1601
1686
  toggle: function toggle() {
1602
- return _this15.setState({
1603
- openDropdown: _this15.isOpen(aggDropdownKey) ? false : aggDropdownKey
1604
- });
1687
+ return setOpenDropdown(isOpen(valDropdownKey) ? false : valDropdownKey);
1605
1688
  },
1606
1689
  setValue: function setValue(value) {
1607
- return _this15.setAggregationAggregator(idx, value);
1608
- }
1609
- }),
1610
- numValsAllowed > 0 && _react2.default.createElement(
1611
- 'div',
1612
- { className: 'pvtAggregatorVals' },
1613
- new Array(numValsAllowed).fill().map(function (n, i) {
1614
- var valDropdownKey = 'aggregation-' + agg.key + '-val-' + i;
1615
- return _react2.default.createElement(Dropdown, {
1616
- key: 'agg-val-' + agg.key + '-' + i,
1617
- current: vals[i],
1618
- values: attributeOptions,
1619
- open: _this15.isOpen(valDropdownKey),
1620
- zIndex: _this15.isOpen(valDropdownKey) ? _this15.state.maxZIndex + 1 : 1,
1621
- toggle: function toggle() {
1622
- return _this15.setState({
1623
- openDropdown: _this15.isOpen(valDropdownKey) ? false : valDropdownKey
1624
- });
1625
- },
1626
- setValue: function setValue(value) {
1627
- return _this15.setAggregationVal(idx, i, value);
1628
- }
1629
- });
1630
- })
1631
- ),
1632
- aggregations.length > 1 && _react2.default.createElement(
1633
- 'button',
1634
- {
1635
- type: 'button',
1636
- className: 'pvtButton pvtRemoveAggregator',
1637
- onClick: function onClick(e) {
1638
- e.stopPropagation();
1639
- _this15.removeAggregationRow(idx);
1640
- }
1641
- },
1642
- 'Remove'
1643
- )
1644
- );
1645
- }),
1646
- _react2.default.createElement(
1647
- 'div',
1648
- { className: 'pvtAggregationModeToggle' },
1649
- _react2.default.createElement(
1650
- 'label',
1651
- {
1652
- htmlFor: 'pvtAggregationMode',
1653
- style: { marginRight: '6px', fontSize: '12px' }
1654
- },
1655
- 'Aggregation values on:'
1656
- ),
1657
- _react2.default.createElement(
1658
- 'select',
1659
- {
1660
- id: 'pvtAggregationMode',
1661
- value: aggregationDisplayMode,
1662
- onChange: function onChange(e) {
1663
- return _this15.setAggregationDisplayMode(e.target.value);
1690
+ return setAggregationVal(idx, i, value);
1664
1691
  }
1665
- },
1666
- _react2.default.createElement(
1667
- 'option',
1668
- { value: 'row' },
1669
- 'Rows'
1670
- ),
1671
- _react2.default.createElement(
1672
- 'option',
1673
- { value: 'column' },
1674
- 'Columns'
1675
- )
1676
- )
1692
+ });
1693
+ })
1677
1694
  ),
1678
- _react2.default.createElement(
1695
+ aggregations.length > 1 && _react2.default.createElement(
1679
1696
  'button',
1680
1697
  {
1681
1698
  type: 'button',
1682
- className: 'pvtButton pvtAddAggregator',
1699
+ className: 'pvtButton pvtRemoveAggregator',
1683
1700
  onClick: function onClick(e) {
1684
1701
  e.stopPropagation();
1685
- _this15.addAggregationRow();
1702
+ removeAggregationRow(idx);
1686
1703
  }
1687
1704
  },
1688
- '+ Add Value'
1689
- ),
1705
+ 'Remove'
1706
+ )
1707
+ );
1708
+ }),
1709
+ _react2.default.createElement(
1710
+ 'div',
1711
+ { className: 'pvtAggregationModeToggle' },
1712
+ _react2.default.createElement(
1713
+ 'label',
1714
+ {
1715
+ htmlFor: 'pvtAggregationMode',
1716
+ style: { marginRight: '6px', fontSize: '12px' }
1717
+ },
1718
+ 'Aggregation values on:'
1719
+ ),
1720
+ _react2.default.createElement(
1721
+ 'select',
1722
+ {
1723
+ id: 'pvtAggregationMode',
1724
+ value: aggregationDisplayMode,
1725
+ onChange: function onChange(e) {
1726
+ return setAggregationDisplayMode(e.target.value);
1727
+ }
1728
+ },
1690
1729
  _react2.default.createElement(
1691
- 'a',
1692
- {
1693
- role: 'button',
1694
- className: 'pvtRowOrder',
1695
- onClick: function onClick() {
1696
- return _this15.propUpdater('rowOrder')(sortIcons[_this15.props.rowOrder].next);
1697
- }
1698
- },
1699
- sortIcons[this.props.rowOrder].rowSymbol
1730
+ 'option',
1731
+ { value: 'row' },
1732
+ 'Rows'
1700
1733
  ),
1701
1734
  _react2.default.createElement(
1702
- 'a',
1703
- {
1704
- role: 'button',
1705
- className: 'pvtColOrder',
1706
- onClick: function onClick() {
1707
- return _this15.propUpdater('colOrder')(sortIcons[_this15.props.colOrder].next);
1708
- }
1709
- },
1710
- sortIcons[this.props.colOrder].colSymbol
1735
+ 'option',
1736
+ { value: 'column' },
1737
+ 'Columns'
1738
+ )
1739
+ )
1740
+ ),
1741
+ _react2.default.createElement(
1742
+ 'button',
1743
+ {
1744
+ type: 'button',
1745
+ className: 'pvtButton pvtAddAggregator',
1746
+ onClick: function onClick(e) {
1747
+ e.stopPropagation();
1748
+ addAggregationRow();
1749
+ }
1750
+ },
1751
+ '+ Add Value'
1752
+ ),
1753
+ _react2.default.createElement(
1754
+ 'a',
1755
+ {
1756
+ role: 'button',
1757
+ className: 'pvtRowOrder',
1758
+ onClick: function onClick() {
1759
+ return propUpdater('rowOrder')(sortIcons[props.rowOrder].next);
1760
+ }
1761
+ },
1762
+ sortIcons[props.rowOrder].rowSymbol
1763
+ ),
1764
+ _react2.default.createElement(
1765
+ 'a',
1766
+ {
1767
+ role: 'button',
1768
+ className: 'pvtColOrder',
1769
+ onClick: function onClick() {
1770
+ return propUpdater('colOrder')(sortIcons[props.colOrder].next);
1771
+ }
1772
+ },
1773
+ sortIcons[props.colOrder].colSymbol
1774
+ ),
1775
+ aggregatorCellOutlet && aggregatorCellOutlet(props.data),
1776
+ _react2.default.createElement(ConditionalFormattingUI, {
1777
+ tableOptions: tableOptions,
1778
+ onChange: sendPropUpdate,
1779
+ moveToTop: function moveToTop() {
1780
+ return moveFilterBoxToTop('conditionalFormatting');
1781
+ },
1782
+ zIndex: zIndices.conditionalFormatting || maxZIndex
1783
+ }),
1784
+ _react2.default.createElement(CellFormattingUI, {
1785
+ tableOptions: tableOptions,
1786
+ onChange: sendPropUpdate,
1787
+ moveToTop: function moveToTop() {
1788
+ return moveFilterBoxToTop('cellFormatting');
1789
+ },
1790
+ zIndex: zIndices.cellFormatting || maxZIndex
1791
+ })
1792
+ );
1793
+
1794
+ var unusedAttrs = Object.keys(attrValues).filter(function (e) {
1795
+ return !props.rows.includes(e) && !props.cols.includes(e) && !props.hiddenAttributes.includes(e) && !props.hiddenFromDragDrop.includes(e);
1796
+ }).sort((0, _Utilities.sortAs)(unusedOrder));
1797
+
1798
+ var unusedLength = unusedAttrs.reduce(function (r, e) {
1799
+ return r + e.length;
1800
+ }, 0);
1801
+ var horizUnused = unusedLength < props.unusedOrientationCutoff;
1802
+
1803
+ var unusedAttrsCell = makeDnDCell(unusedAttrs, function (order) {
1804
+ return setUnusedOrder(order);
1805
+ }, 'pvtAxisContainer pvtUnused ' + (horizUnused ? 'pvtHorizList' : 'pvtVertList'));
1806
+
1807
+ var colAttrs = Array.from(new Set(props.cols)).filter(function (e) {
1808
+ return !props.hiddenAttributes.includes(e) && !props.hiddenFromDragDrop.includes(e);
1809
+ });
1810
+
1811
+ var colAttrsCell = makeDnDCell(colAttrs, function (order) {
1812
+ return setAxisFromDrag('cols', order);
1813
+ }, 'pvtAxisContainer pvtHorizList pvtCols');
1814
+
1815
+ var rowAttrs = Array.from(new Set(props.rows)).filter(function (e) {
1816
+ return !props.hiddenAttributes.includes(e) && !props.hiddenFromDragDrop.includes(e);
1817
+ });
1818
+ var rowAttrsCell = makeDnDCell(rowAttrs, function (order) {
1819
+ return setAxisFromDrag('rows', order);
1820
+ }, 'pvtAxisContainer pvtVertList pvtRows');
1821
+
1822
+ // `isShowProcessing` is a testing hook: when true it forces the loading
1823
+ // overlay on top of the table regardless of whether a worker computation
1824
+ // is actually running.
1825
+ var showProcessing = (isProcessing || props.isShowProcessing) && !computeError;
1826
+ // While recomputing, re-render the last valid table (old props matched to
1827
+ // their own precomputed result) so the overlay sits on top of real content.
1828
+ var staleView = showProcessing ? lastGoodViewRef.current : null;
1829
+
1830
+ var outputContent = void 0;
1831
+ if (computeError) {
1832
+ outputContent = _react2.default.createElement(
1833
+ 'div',
1834
+ { className: 'pvtComputeError' },
1835
+ _react2.default.createElement(
1836
+ 'p',
1837
+ null,
1838
+ _react2.default.createElement(
1839
+ 'strong',
1840
+ null,
1841
+ 'PivotTable error: '
1711
1842
  ),
1712
- aggregatorCellOutlet && aggregatorCellOutlet(this.props.data),
1713
- _react2.default.createElement(ConditionalFormattingUI, {
1714
- tableOptions: tableOptions,
1715
- onChange: this.sendPropUpdate.bind(this),
1716
- moveToTop: this.moveFilterBoxToTop.bind(this, 'conditionalFormatting'),
1717
- zIndex: this.state.zIndices.conditionalFormatting || this.state.maxZIndex
1718
- }),
1719
- _react2.default.createElement(CellFormattingUI, {
1720
- tableOptions: tableOptions,
1721
- onChange: this.sendPropUpdate.bind(this),
1722
- moveToTop: this.moveFilterBoxToTop.bind(this, 'cellFormatting'),
1723
- zIndex: this.state.zIndices.cellFormatting || this.state.maxZIndex
1724
- })
1725
- );
1726
-
1727
- var unusedAttrs = Object.keys(this.state.attrValues).filter(function (e) {
1728
- return !_this15.props.rows.includes(e) && !_this15.props.cols.includes(e) && !_this15.props.hiddenAttributes.includes(e) && !_this15.props.hiddenFromDragDrop.includes(e);
1729
- }).sort((0, _Utilities.sortAs)(this.state.unusedOrder));
1730
-
1731
- var unusedLength = unusedAttrs.reduce(function (r, e) {
1732
- return r + e.length;
1733
- }, 0);
1734
- var horizUnused = unusedLength < this.props.unusedOrientationCutoff;
1735
-
1736
- var unusedAttrsCell = this.makeDnDCell(unusedAttrs, function (order) {
1737
- return _this15.setState({ unusedOrder: order });
1738
- }, 'pvtAxisContainer pvtUnused ' + (horizUnused ? 'pvtHorizList' : 'pvtVertList'));
1739
-
1740
- var colAttrs = this.props.cols.filter(function (e) {
1741
- return !_this15.props.hiddenAttributes.includes(e) && !_this15.props.hiddenFromDragDrop.includes(e);
1742
- });
1743
-
1744
- var colAttrsCell = this.makeDnDCell(colAttrs, this.propUpdater('cols'), 'pvtAxisContainer pvtHorizList pvtCols');
1745
-
1746
- var rowAttrs = this.props.rows.filter(function (e) {
1747
- return !_this15.props.hiddenAttributes.includes(e) && !_this15.props.hiddenFromDragDrop.includes(e);
1748
- });
1749
- var rowAttrsCell = this.makeDnDCell(rowAttrs, this.propUpdater('rows'), 'pvtAxisContainer pvtVertList pvtRows');
1750
- var outputCell = _react2.default.createElement(
1751
- 'td',
1752
- { className: 'pvtOutput' },
1753
- _react2.default.createElement(_PivotTable2.default, _extends({}, (0, _immutabilityHelper2.default)(this.props, {
1754
- data: { $set: this.state.materializedInput }
1755
- }), {
1756
- colSorts: this.props.colSorts || {},
1757
- rowSorts: this.props.rowSorts || {},
1758
- onColSort: this.handleColSort.bind(this),
1759
- onRowSort: this.handleRowSort.bind(this)
1760
- }))
1761
- );
1762
-
1763
- if (horizUnused) {
1764
- return _react2.default.createElement(
1765
- 'table',
1766
- { className: 'pvtUi' },
1767
- _react2.default.createElement(
1768
- 'tbody',
1769
- { onClick: function onClick() {
1770
- return _this15.setState({ openDropdown: false });
1771
- } },
1772
- _react2.default.createElement(
1773
- 'tr',
1774
- null,
1775
- rendererCell,
1776
- unusedAttrsCell
1777
- ),
1778
- _react2.default.createElement(
1779
- 'tr',
1780
- null,
1781
- aggregatorCell,
1782
- colAttrsCell
1783
- ),
1784
- _react2.default.createElement(
1785
- 'tr',
1786
- null,
1787
- rowAttrsCell,
1788
- outputCell
1789
- )
1790
- )
1791
- );
1792
- }
1843
+ computeError
1844
+ )
1845
+ );
1846
+ } else if (staleView) {
1847
+ outputContent = _react2.default.createElement(_PivotTable2.default, _extends({}, staleView.props, {
1848
+ data: staleView.materializedInput,
1849
+ precomputedData: staleView.precomputed ? staleView.precomputed.data : null,
1850
+ colSorts: staleView.props.colSorts || {},
1851
+ rowSorts: staleView.props.rowSorts || {},
1852
+ onColSort: handleColSort,
1853
+ onRowSort: handleRowSort
1854
+ }));
1855
+ } else if (showProcessing) {
1856
+ // First computation: no previous table to keep visible yet.
1857
+ outputContent = _react2.default.createElement('div', { className: 'pvtOutputPlaceholder' });
1858
+ } else {
1859
+ outputContent = _react2.default.createElement(_PivotTable2.default, _extends({}, props, {
1860
+ data: materializedInput,
1861
+ precomputedData: workerEligible && precomputed ? precomputed.data : props.precomputedData || null,
1862
+ colSorts: props.colSorts || {},
1863
+ rowSorts: props.rowSorts || {},
1864
+ onColSort: handleColSort,
1865
+ onRowSort: handleRowSort
1866
+ }));
1867
+ }
1793
1868
 
1794
- return _react2.default.createElement(
1795
- 'table',
1796
- { className: 'pvtUi' },
1869
+ // With the `isShowProcessing` testing hook active and no real computation
1870
+ // running there is no progress to report, so feed the overlay a sample
1871
+ // value to exercise its progress-bar and percentage branch as well.
1872
+ var overlayProgress = props.isShowProcessing && !isProcessing ? progress || { processed: 60, total: 100, phase: 'aggregate' } : progress;
1873
+
1874
+ var outputCell = _react2.default.createElement(
1875
+ 'td',
1876
+ { className: 'pvtOutput' },
1877
+ outputContent,
1878
+ showProcessing ? _react2.default.createElement(PivotLoadingOverlay, { progress: overlayProgress }) : null
1879
+ );
1880
+
1881
+ if (horizUnused) {
1882
+ return _react2.default.createElement(
1883
+ 'table',
1884
+ { className: 'pvtUi' },
1885
+ _react2.default.createElement(
1886
+ 'tbody',
1887
+ { onClick: function onClick() {
1888
+ return setOpenDropdown(false);
1889
+ } },
1797
1890
  _react2.default.createElement(
1798
- 'tbody',
1799
- { onClick: function onClick() {
1800
- return _this15.setState({ openDropdown: false });
1801
- } },
1802
- _react2.default.createElement(
1803
- 'tr',
1804
- null,
1805
- rendererCell,
1806
- aggregatorCell,
1807
- colAttrsCell
1808
- ),
1809
- _react2.default.createElement(
1810
- 'tr',
1811
- null,
1812
- unusedAttrsCell,
1813
- rowAttrsCell,
1814
- outputCell
1815
- )
1891
+ 'tr',
1892
+ null,
1893
+ rendererCell,
1894
+ unusedAttrsCell
1895
+ ),
1896
+ _react2.default.createElement(
1897
+ 'tr',
1898
+ null,
1899
+ aggregatorCell,
1900
+ colAttrsCell
1901
+ ),
1902
+ _react2.default.createElement(
1903
+ 'tr',
1904
+ null,
1905
+ rowAttrsCell,
1906
+ outputCell
1816
1907
  )
1817
- );
1818
- }
1819
- }]);
1908
+ )
1909
+ );
1910
+ }
1820
1911
 
1821
- return PivotTableUI;
1822
- }(_react2.default.PureComponent);
1912
+ return _react2.default.createElement(
1913
+ 'table',
1914
+ { className: 'pvtUi' },
1915
+ _react2.default.createElement(
1916
+ 'tbody',
1917
+ { onClick: function onClick() {
1918
+ return setOpenDropdown(false);
1919
+ } },
1920
+ _react2.default.createElement(
1921
+ 'tr',
1922
+ null,
1923
+ rendererCell,
1924
+ aggregatorCell,
1925
+ colAttrsCell
1926
+ ),
1927
+ _react2.default.createElement(
1928
+ 'tr',
1929
+ null,
1930
+ unusedAttrsCell,
1931
+ rowAttrsCell,
1932
+ outputCell
1933
+ )
1934
+ )
1935
+ );
1936
+ }
1823
1937
 
1824
1938
  PivotTableUI.propTypes = Object.assign({}, _PivotTable2.default.propTypes, {
1825
1939
  onChange: _propTypes2.default.func.isRequired,
@@ -1829,7 +1943,11 @@ PivotTableUI.propTypes = Object.assign({}, _PivotTable2.default.propTypes, {
1829
1943
  unusedOrientationCutoff: _propTypes2.default.number,
1830
1944
  menuLimit: _propTypes2.default.number,
1831
1945
  colSorts: _propTypes2.default.objectOf(_propTypes2.default.oneOf(['ASC', 'DESC'])),
1832
- rowSorts: _propTypes2.default.objectOf(_propTypes2.default.oneOf(['ASC', 'DESC']))
1946
+ rowSorts: _propTypes2.default.objectOf(_propTypes2.default.oneOf(['ASC', 'DESC'])),
1947
+ enableWorker: _propTypes2.default.bool,
1948
+ workerThreshold: _propTypes2.default.number,
1949
+ workerUrl: _propTypes2.default.string,
1950
+ isShowProcessing: _propTypes2.default.bool
1833
1951
  });
1834
1952
 
1835
1953
  PivotTableUI.defaultProps = Object.assign({}, _PivotTable2.default.defaultProps, {
@@ -1839,7 +1957,11 @@ PivotTableUI.defaultProps = Object.assign({}, _PivotTable2.default.defaultProps,
1839
1957
  unusedOrientationCutoff: 85,
1840
1958
  menuLimit: 500,
1841
1959
  colSorts: {},
1842
- rowSorts: {}
1960
+ rowSorts: {},
1961
+ enableWorker: true,
1962
+ workerThreshold: _pivotCompute.DEFAULT_WORKER_THRESHOLD,
1963
+ workerUrl: _PivotDataWorker.DEFAULT_WORKER_URL,
1964
+ isShowProcessing: false
1843
1965
  });
1844
1966
 
1845
1967
  exports.default = PivotTableUI;