@hrforte/react-pivottable 1.1.0 → 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.
- package/README.md +45 -0
- package/dist/PivotDataWorker.js +164 -0
- package/dist/PivotDataWorker.js.map +1 -0
- package/dist/PivotTableUI.js +1734 -1612
- package/dist/PivotTableUI.js.map +1 -1
- package/dist/PlotlyRenderers.js +3 -1
- package/dist/PlotlyRenderers.js.map +1 -1
- package/dist/Utilities.js +134 -24
- package/dist/Utilities.js.map +1 -1
- package/dist/__tests__/PivotTableUI-processing-test.js +114 -0
- package/dist/__tests__/PivotTableUI-processing-test.js.map +1 -0
- package/dist/__tests__/pivotCompute-test.js +215 -0
- package/dist/__tests__/pivotCompute-test.js.map +1 -0
- package/dist/pivot-worker.js +3387 -0
- package/dist/pivotCompute.js +274 -0
- package/dist/pivotCompute.js.map +1 -0
- package/dist/pivotWorker.js +91 -0
- package/dist/pivotWorker.js.map +1 -0
- package/dist/pivottable.css +74 -0
- package/package.json +4 -3
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.WORKER_DEBOUNCE_MS = exports.DEFAULT_WORKER_THRESHOLD = exports.PROGRESS_BATCH_SIZE = undefined;
|
|
7
|
+
|
|
8
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /* eslint-disable valid-jsdoc */
|
|
9
|
+
/*
|
|
10
|
+
* Shared pivot-computation logic.
|
|
11
|
+
*
|
|
12
|
+
* This module contains the pure, DOM-free parts of the pivot computation so
|
|
13
|
+
* that the exact same code paths can run:
|
|
14
|
+
* - on the main thread (classic synchronous rendering path), and
|
|
15
|
+
* - inside a Web Worker (src/pivotWorker.js) for large datasets.
|
|
16
|
+
*
|
|
17
|
+
* It must stay free of React and of any `window`/`document` references.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
exports.materializeData = materializeData;
|
|
22
|
+
exports.computePivotState = computePivotState;
|
|
23
|
+
exports.serializePivotData = serializePivotData;
|
|
24
|
+
exports.sortersHaveFunctions = sortersHaveFunctions;
|
|
25
|
+
exports.sanitizeSorters = sanitizeSorters;
|
|
26
|
+
exports.createWorkerConfig = createWorkerConfig;
|
|
27
|
+
exports.createComputeSignature = createComputeSignature;
|
|
28
|
+
exports.canComputeInWorker = canComputeInWorker;
|
|
29
|
+
|
|
30
|
+
var _Utilities = require('./Utilities');
|
|
31
|
+
|
|
32
|
+
// A progress callback fires every N processed records so the UI can render
|
|
33
|
+
// progress without adding measurable overhead to the per-record loop.
|
|
34
|
+
var PROGRESS_BATCH_SIZE = exports.PROGRESS_BATCH_SIZE = 20000;
|
|
35
|
+
|
|
36
|
+
// Datasets smaller than this are computed synchronously on the main thread;
|
|
37
|
+
// larger ones are handed off to the Web Worker.
|
|
38
|
+
var DEFAULT_WORKER_THRESHOLD = exports.DEFAULT_WORKER_THRESHOLD = 10000;
|
|
39
|
+
|
|
40
|
+
// Trailing debounce before a worker computation is dispatched, so rapid
|
|
41
|
+
// interactions do not queue one full computation per single change.
|
|
42
|
+
var WORKER_DEBOUNCE_MS = exports.WORKER_DEBOUNCE_MS = 200;
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* Walks the raw input once and computes the attribute-value inventory plus a
|
|
46
|
+
* "materialized" copy of the records (with derived attributes applied).
|
|
47
|
+
* This is an exact extraction of the historical PivotTableUI behavior.
|
|
48
|
+
*
|
|
49
|
+
* onProgress receives (processed, total, phase); total is null when it is
|
|
50
|
+
* unknown upfront (function-based data sources).
|
|
51
|
+
*/
|
|
52
|
+
function materializeData(data, derivedAttributes, onProgress) {
|
|
53
|
+
var attrValues = {};
|
|
54
|
+
var materializedInput = [];
|
|
55
|
+
var total = Array.isArray(data) ? data.length : null;
|
|
56
|
+
var recordsProcessed = 0;
|
|
57
|
+
_Utilities.PivotData.forEachRecord(data, derivedAttributes || {}, function (record) {
|
|
58
|
+
materializedInput.push(record);
|
|
59
|
+
var _iteratorNormalCompletion = true;
|
|
60
|
+
var _didIteratorError = false;
|
|
61
|
+
var _iteratorError = undefined;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
for (var _iterator = Object.keys(record)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
65
|
+
var attr = _step.value;
|
|
66
|
+
|
|
67
|
+
if (!(attr in attrValues)) {
|
|
68
|
+
attrValues[attr] = {};
|
|
69
|
+
if (recordsProcessed > 0) {
|
|
70
|
+
attrValues[attr].null = recordsProcessed;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
_didIteratorError = true;
|
|
76
|
+
_iteratorError = err;
|
|
77
|
+
} finally {
|
|
78
|
+
try {
|
|
79
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
80
|
+
_iterator.return();
|
|
81
|
+
}
|
|
82
|
+
} finally {
|
|
83
|
+
if (_didIteratorError) {
|
|
84
|
+
throw _iteratorError;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (var _attr in attrValues) {
|
|
90
|
+
var value = _attr in record ? record[_attr] : 'null';
|
|
91
|
+
if (!(value in attrValues[_attr])) {
|
|
92
|
+
attrValues[_attr][value] = 0;
|
|
93
|
+
}
|
|
94
|
+
attrValues[_attr][value]++;
|
|
95
|
+
}
|
|
96
|
+
recordsProcessed++;
|
|
97
|
+
if (onProgress && recordsProcessed % PROGRESS_BATCH_SIZE === 0) {
|
|
98
|
+
onProgress(recordsProcessed, total, 'materialize');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return { attrValues: attrValues, materializedInput: materializedInput, totalRecords: recordsProcessed };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/*
|
|
105
|
+
* Aggregates an already-materialized dataset into a PivotData instance and
|
|
106
|
+
* serializes the result into a plain (structured-cloneable) object that can
|
|
107
|
+
* cross the worker boundary and later hydrate PivotData on the main thread.
|
|
108
|
+
*/
|
|
109
|
+
function computePivotState(materialized, config, onProgress) {
|
|
110
|
+
var total = materialized.totalRecords;
|
|
111
|
+
var pivotData = new _Utilities.PivotData(Object.assign({}, config, {
|
|
112
|
+
data: materialized.materializedInput,
|
|
113
|
+
derivedAttributes: {},
|
|
114
|
+
onRecordProcessed: function onRecordProcessed(processed) {
|
|
115
|
+
if (onProgress) {
|
|
116
|
+
onProgress(processed, total, 'aggregate');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}));
|
|
120
|
+
// Force key sorting to happen while we are (possibly) inside the worker.
|
|
121
|
+
pivotData.getRowKeys();
|
|
122
|
+
pivotData.getColKeys();
|
|
123
|
+
return serializePivotData(pivotData, materialized);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* Serializes a fully-built PivotData instance into plain data. Only the final
|
|
128
|
+
* aggregated value of every cell is kept; `value()` and `format()` behavior
|
|
129
|
+
* are reconstructed on the main thread by the PivotData hydration step.
|
|
130
|
+
*/
|
|
131
|
+
function serializePivotData(pivotData, materialized) {
|
|
132
|
+
var serializeCell = function serializeCell(cell) {
|
|
133
|
+
var values = {};
|
|
134
|
+
pivotData.aggregations.forEach(function (agg) {
|
|
135
|
+
var instance = cell.getInstance(agg.key);
|
|
136
|
+
if (instance) {
|
|
137
|
+
values[agg.key] = instance.value();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return { values: values };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
var tree = {};
|
|
144
|
+
Object.keys(pivotData.tree).forEach(function (flatRowKey) {
|
|
145
|
+
var rowBranch = pivotData.tree[flatRowKey];
|
|
146
|
+
var serializedRow = {};
|
|
147
|
+
Object.keys(rowBranch).forEach(function (flatColKey) {
|
|
148
|
+
serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);
|
|
149
|
+
});
|
|
150
|
+
tree[flatRowKey] = serializedRow;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
var serializeTotals = function serializeTotals(totals) {
|
|
154
|
+
var serialized = {};
|
|
155
|
+
Object.keys(totals).forEach(function (flatKey) {
|
|
156
|
+
serialized[flatKey] = serializeCell(totals[flatKey]);
|
|
157
|
+
});
|
|
158
|
+
return serialized;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
rowKeys: pivotData.rowKeys.map(function (key) {
|
|
163
|
+
return key.slice();
|
|
164
|
+
}),
|
|
165
|
+
colKeys: pivotData.colKeys.map(function (key) {
|
|
166
|
+
return key.slice();
|
|
167
|
+
}),
|
|
168
|
+
tree: tree,
|
|
169
|
+
rowTotals: serializeTotals(pivotData.rowTotals),
|
|
170
|
+
colTotals: serializeTotals(pivotData.colTotals),
|
|
171
|
+
allTotal: serializeCell(pivotData.allTotal),
|
|
172
|
+
aggregations: pivotData.aggregations.map(function (agg) {
|
|
173
|
+
return Object.assign({}, agg);
|
|
174
|
+
}),
|
|
175
|
+
attrValues: materialized.attrValues,
|
|
176
|
+
materializedInput: materialized.materializedInput,
|
|
177
|
+
totalRecords: materialized.totalRecords
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/*
|
|
182
|
+
* Sorters containing functions cannot be structured-cloned into a worker.
|
|
183
|
+
* Returns true when the sorters prop itself, or any of its entries, is a
|
|
184
|
+
* function. In that case PivotData re-sorts on the main thread after
|
|
185
|
+
* hydration (distinct keys are few, so this stays cheap).
|
|
186
|
+
*/
|
|
187
|
+
function sortersHaveFunctions(sorters) {
|
|
188
|
+
if (typeof sorters === 'function') {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (!sorters || (typeof sorters === 'undefined' ? 'undefined' : _typeof(sorters)) !== 'object') {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return Object.keys(sorters).some(function (attr) {
|
|
195
|
+
return typeof sorters[attr] === 'function';
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/*
|
|
200
|
+
* Returns a structured-cloneable subset of the sorters prop: data-type names
|
|
201
|
+
* and sample-value arrays survive the worker boundary, functions do not.
|
|
202
|
+
*/
|
|
203
|
+
function sanitizeSorters(sorters) {
|
|
204
|
+
if (!sorters || (typeof sorters === 'undefined' ? 'undefined' : _typeof(sorters)) !== 'object' || typeof sorters === 'function') {
|
|
205
|
+
return {};
|
|
206
|
+
}
|
|
207
|
+
var clean = {};
|
|
208
|
+
Object.keys(sorters).forEach(function (attr) {
|
|
209
|
+
var sorter = sorters[attr];
|
|
210
|
+
if (typeof sorter === 'string' || Array.isArray(sorter)) {
|
|
211
|
+
clean[attr] = sorter;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return clean;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/*
|
|
218
|
+
* Extracts exactly the props PivotData needs to compute results, with
|
|
219
|
+
* everything non-serializable stripped, so the object can be posted to the
|
|
220
|
+
* worker and fed straight into PivotData.
|
|
221
|
+
*/
|
|
222
|
+
function createWorkerConfig(props) {
|
|
223
|
+
return {
|
|
224
|
+
rows: props.rows,
|
|
225
|
+
cols: props.cols,
|
|
226
|
+
aggregatorName: props.aggregatorName,
|
|
227
|
+
aggregations: props.aggregations,
|
|
228
|
+
vals: props.vals,
|
|
229
|
+
valueFilter: props.valueFilter,
|
|
230
|
+
sorters: sanitizeSorters(props.sorters),
|
|
231
|
+
rowOrder: props.rowOrder,
|
|
232
|
+
colOrder: props.colOrder,
|
|
233
|
+
rowSorts: props.rowSorts,
|
|
234
|
+
colSorts: props.colSorts
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/*
|
|
239
|
+
* Stable string signature of everything that influences a worker computation.
|
|
240
|
+
* Data identity is NOT part of it; callers append their own data version.
|
|
241
|
+
*/
|
|
242
|
+
function createComputeSignature(props) {
|
|
243
|
+
return JSON.stringify(createWorkerConfig(props));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/*
|
|
247
|
+
* Decides whether the current props can be computed inside the Web Worker.
|
|
248
|
+
* Anything that cannot be structured-cloned (function data sources, custom
|
|
249
|
+
* derivedAttributes, custom aggregator maps) forces the classic path.
|
|
250
|
+
* Function-based sorters are allowed: they are re-applied on the main thread.
|
|
251
|
+
*/
|
|
252
|
+
function canComputeInWorker(props) {
|
|
253
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
254
|
+
|
|
255
|
+
if (options.enabled === false) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
if (typeof Worker === 'undefined') {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
var threshold = typeof options.threshold === 'number' ? options.threshold : DEFAULT_WORKER_THRESHOLD;
|
|
262
|
+
var data = props.data;
|
|
263
|
+
if (!Array.isArray(data) || data.length < threshold) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
if (props.derivedAttributes && Object.keys(props.derivedAttributes).length > 0) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
if (props.aggregators !== _Utilities.PivotData.defaultProps.aggregators) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=pivotCompute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pivotCompute.js"],"names":["materializeData","computePivotState","serializePivotData","sortersHaveFunctions","sanitizeSorters","createWorkerConfig","createComputeSignature","canComputeInWorker","PROGRESS_BATCH_SIZE","DEFAULT_WORKER_THRESHOLD","WORKER_DEBOUNCE_MS","data","derivedAttributes","onProgress","attrValues","materializedInput","total","Array","isArray","length","recordsProcessed","PivotData","forEachRecord","record","push","Object","keys","attr","null","value","totalRecords","materialized","config","pivotData","assign","onRecordProcessed","processed","getRowKeys","getColKeys","serializeCell","values","aggregations","forEach","instance","cell","getInstance","agg","key","tree","rowBranch","flatRowKey","serializedRow","flatColKey","serializeTotals","serialized","totals","flatKey","rowKeys","map","slice","colKeys","rowTotals","colTotals","allTotal","sorters","some","clean","sorter","props","rows","cols","aggregatorName","vals","valueFilter","rowOrder","colOrder","rowSorts","colSorts","JSON","stringify","options","enabled","Worker","threshold","aggregators","defaultProps"],"mappings":"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","file":"pivotCompute.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/*\n * Shared pivot-computation logic.\n *\n * This module contains the pure, DOM-free parts of the pivot computation so\n * that the exact same code paths can run:\n * - on the main thread (classic synchronous rendering path), and\n * - inside a Web Worker (src/pivotWorker.js) for large datasets.\n *\n * It must stay free of React and of any `window`/`document` references.\n */\nimport {PivotData} from './Utilities';\n\n// A progress callback fires every N processed records so the UI can render\n// progress without adding measurable overhead to the per-record loop.\nexport const PROGRESS_BATCH_SIZE = 20000;\n\n// Datasets smaller than this are computed synchronously on the main thread;\n// larger ones are handed off to the Web Worker.\nexport const DEFAULT_WORKER_THRESHOLD = 10000;\n\n// Trailing debounce before a worker computation is dispatched, so rapid\n// interactions do not queue one full computation per single change.\nexport const WORKER_DEBOUNCE_MS = 200;\n\n/*\n * Walks the raw input once and computes the attribute-value inventory plus a\n * \"materialized\" copy of the records (with derived attributes applied).\n * This is an exact extraction of the historical PivotTableUI behavior.\n *\n * onProgress receives (processed, total, phase); total is null when it is\n * unknown upfront (function-based data sources).\n */\nexport function materializeData(data, derivedAttributes, onProgress) {\n const attrValues = {};\n const materializedInput = [];\n const total = Array.isArray(data) ? data.length : null;\n let recordsProcessed = 0;\n PivotData.forEachRecord(data, derivedAttributes || {}, function(record) {\n materializedInput.push(record);\n for (const attr of Object.keys(record)) {\n if (!(attr in attrValues)) {\n attrValues[attr] = {};\n if (recordsProcessed > 0) {\n attrValues[attr].null = recordsProcessed;\n }\n }\n }\n for (const attr in attrValues) {\n const value = attr in record ? record[attr] : 'null';\n if (!(value in attrValues[attr])) {\n attrValues[attr][value] = 0;\n }\n attrValues[attr][value]++;\n }\n recordsProcessed++;\n if (onProgress && recordsProcessed % PROGRESS_BATCH_SIZE === 0) {\n onProgress(recordsProcessed, total, 'materialize');\n }\n });\n return {attrValues, materializedInput, totalRecords: recordsProcessed};\n}\n\n/*\n * Aggregates an already-materialized dataset into a PivotData instance and\n * serializes the result into a plain (structured-cloneable) object that can\n * cross the worker boundary and later hydrate PivotData on the main thread.\n */\nexport function computePivotState(materialized, config, onProgress) {\n const total = materialized.totalRecords;\n const pivotData = new PivotData(\n Object.assign({}, config, {\n data: materialized.materializedInput,\n derivedAttributes: {},\n onRecordProcessed: processed => {\n if (onProgress) {\n onProgress(processed, total, 'aggregate');\n }\n },\n })\n );\n // Force key sorting to happen while we are (possibly) inside the worker.\n pivotData.getRowKeys();\n pivotData.getColKeys();\n return serializePivotData(pivotData, materialized);\n}\n\n/*\n * Serializes a fully-built PivotData instance into plain data. Only the final\n * aggregated value of every cell is kept; `value()` and `format()` behavior\n * are reconstructed on the main thread by the PivotData hydration step.\n */\nexport function serializePivotData(pivotData, materialized) {\n const serializeCell = cell => {\n const values = {};\n pivotData.aggregations.forEach(agg => {\n const instance = cell.getInstance(agg.key);\n if (instance) {\n values[agg.key] = instance.value();\n }\n });\n return {values};\n };\n\n const tree = {};\n Object.keys(pivotData.tree).forEach(flatRowKey => {\n const rowBranch = pivotData.tree[flatRowKey];\n const serializedRow = {};\n Object.keys(rowBranch).forEach(flatColKey => {\n serializedRow[flatColKey] = serializeCell(rowBranch[flatColKey]);\n });\n tree[flatRowKey] = serializedRow;\n });\n\n const serializeTotals = totals => {\n const serialized = {};\n Object.keys(totals).forEach(flatKey => {\n serialized[flatKey] = serializeCell(totals[flatKey]);\n });\n return serialized;\n };\n\n return {\n rowKeys: pivotData.rowKeys.map(key => key.slice()),\n colKeys: pivotData.colKeys.map(key => key.slice()),\n tree,\n rowTotals: serializeTotals(pivotData.rowTotals),\n colTotals: serializeTotals(pivotData.colTotals),\n allTotal: serializeCell(pivotData.allTotal),\n aggregations: pivotData.aggregations.map(agg => Object.assign({}, agg)),\n attrValues: materialized.attrValues,\n materializedInput: materialized.materializedInput,\n totalRecords: materialized.totalRecords,\n };\n}\n\n/*\n * Sorters containing functions cannot be structured-cloned into a worker.\n * Returns true when the sorters prop itself, or any of its entries, is a\n * function. In that case PivotData re-sorts on the main thread after\n * hydration (distinct keys are few, so this stays cheap).\n */\nexport function sortersHaveFunctions(sorters) {\n if (typeof sorters === 'function') {\n return true;\n }\n if (!sorters || typeof sorters !== 'object') {\n return false;\n }\n return Object.keys(sorters).some(attr => typeof sorters[attr] === 'function');\n}\n\n/*\n * Returns a structured-cloneable subset of the sorters prop: data-type names\n * and sample-value arrays survive the worker boundary, functions do not.\n */\nexport function sanitizeSorters(sorters) {\n if (\n !sorters ||\n typeof sorters !== 'object' ||\n typeof sorters === 'function'\n ) {\n return {};\n }\n const clean = {};\n Object.keys(sorters).forEach(attr => {\n const sorter = sorters[attr];\n if (typeof sorter === 'string' || Array.isArray(sorter)) {\n clean[attr] = sorter;\n }\n });\n return clean;\n}\n\n/*\n * Extracts exactly the props PivotData needs to compute results, with\n * everything non-serializable stripped, so the object can be posted to the\n * worker and fed straight into PivotData.\n */\nexport function createWorkerConfig(props) {\n return {\n rows: props.rows,\n cols: props.cols,\n aggregatorName: props.aggregatorName,\n aggregations: props.aggregations,\n vals: props.vals,\n valueFilter: props.valueFilter,\n sorters: sanitizeSorters(props.sorters),\n rowOrder: props.rowOrder,\n colOrder: props.colOrder,\n rowSorts: props.rowSorts,\n colSorts: props.colSorts,\n };\n}\n\n/*\n * Stable string signature of everything that influences a worker computation.\n * Data identity is NOT part of it; callers append their own data version.\n */\nexport function createComputeSignature(props) {\n return JSON.stringify(createWorkerConfig(props));\n}\n\n/*\n * Decides whether the current props can be computed inside the Web Worker.\n * Anything that cannot be structured-cloned (function data sources, custom\n * derivedAttributes, custom aggregator maps) forces the classic path.\n * Function-based sorters are allowed: they are re-applied on the main thread.\n */\nexport function canComputeInWorker(props, options = {}) {\n if (options.enabled === false) {\n return false;\n }\n if (typeof Worker === 'undefined') {\n return false;\n }\n const threshold =\n typeof options.threshold === 'number'\n ? options.threshold\n : DEFAULT_WORKER_THRESHOLD;\n const data = props.data;\n if (!Array.isArray(data) || data.length < threshold) {\n return false;\n }\n if (\n props.derivedAttributes &&\n Object.keys(props.derivedAttributes).length > 0\n ) {\n return false;\n }\n if (props.aggregators !== PivotData.defaultProps.aggregators) {\n return false;\n }\n return true;\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _pivotCompute = require('./pivotCompute');
|
|
4
|
+
|
|
5
|
+
var MAX_CACHED_DATASETS = 4; /* eslint-disable valid-jsdoc */
|
|
6
|
+
/*
|
|
7
|
+
* Web Worker entry point for off-main-thread pivot computation.
|
|
8
|
+
*
|
|
9
|
+
* Message protocol (plain, structured-cloneable objects):
|
|
10
|
+
* main -> worker {type: 'pivot:setData', id, dataKey, data}
|
|
11
|
+
* main -> worker {type: 'pivot:compute', id, dataKey, config}
|
|
12
|
+
* worker -> main {type: 'pivot:progress', id, processed, total, phase}
|
|
13
|
+
* worker -> main {type: 'pivot:result', id, result}
|
|
14
|
+
* worker -> main {type: 'pivot:error', id, message}
|
|
15
|
+
*
|
|
16
|
+
* The materialized dataset is cached per dataKey, so repeated computations on
|
|
17
|
+
* the same data (e.g. dragging fields around) do not pay the cost of
|
|
18
|
+
* re-scanning the raw records.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
var datasetCache = new Map();
|
|
23
|
+
|
|
24
|
+
var cacheDataset = function cacheDataset(dataKey, dataset) {
|
|
25
|
+
datasetCache.delete(dataKey);
|
|
26
|
+
datasetCache.set(dataKey, dataset);
|
|
27
|
+
while (datasetCache.size > MAX_CACHED_DATASETS) {
|
|
28
|
+
var oldestKey = datasetCache.keys().next().value;
|
|
29
|
+
datasetCache.delete(oldestKey);
|
|
30
|
+
}
|
|
31
|
+
return dataset;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var postToMain = function postToMain(message) {
|
|
35
|
+
self.postMessage(message);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
var toErrorMessage = function toErrorMessage(error) {
|
|
39
|
+
return String(error && error.message || error);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
self.addEventListener('message', function (event) {
|
|
43
|
+
var message = event.data || {};
|
|
44
|
+
|
|
45
|
+
if (message.type === 'pivot:setData') {
|
|
46
|
+
try {
|
|
47
|
+
cacheDataset(message.dataKey, (0, _pivotCompute.materializeData)(message.data, {}, function (processed, total) {
|
|
48
|
+
postToMain({
|
|
49
|
+
type: 'pivot:progress',
|
|
50
|
+
id: message.id,
|
|
51
|
+
processed: processed,
|
|
52
|
+
total: total,
|
|
53
|
+
phase: 'materialize'
|
|
54
|
+
});
|
|
55
|
+
}));
|
|
56
|
+
} catch (error) {
|
|
57
|
+
postToMain({
|
|
58
|
+
type: 'pivot:error',
|
|
59
|
+
id: message.id,
|
|
60
|
+
message: toErrorMessage(error)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (message.type === 'pivot:compute') {
|
|
67
|
+
try {
|
|
68
|
+
var dataset = datasetCache.get(message.dataKey);
|
|
69
|
+
if (!dataset) {
|
|
70
|
+
throw new Error('no dataset staged for key ' + message.dataKey);
|
|
71
|
+
}
|
|
72
|
+
var result = (0, _pivotCompute.computePivotState)(dataset, message.config, function (processed, total, phase) {
|
|
73
|
+
postToMain({
|
|
74
|
+
type: 'pivot:progress',
|
|
75
|
+
id: message.id,
|
|
76
|
+
processed: processed,
|
|
77
|
+
total: total,
|
|
78
|
+
phase: phase
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
postToMain({ type: 'pivot:result', id: message.id, result: result });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
postToMain({
|
|
84
|
+
type: 'pivot:error',
|
|
85
|
+
id: message.id,
|
|
86
|
+
message: toErrorMessage(error)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=pivotWorker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/pivotWorker.js"],"names":["MAX_CACHED_DATASETS","datasetCache","Map","cacheDataset","dataKey","dataset","delete","set","size","oldestKey","keys","next","value","postToMain","self","postMessage","message","toErrorMessage","String","error","addEventListener","event","data","type","processed","total","id","phase","get","Error","result","config"],"mappings":";;AAeA;;AAEA,IAAMA,sBAAsB,CAA5B,C,CAjBA;AACA;;;;;;;;;;;;;;;;AAkBA,IAAMC,eAAe,IAAIC,GAAJ,EAArB;;AAEA,IAAMC,eAAe,SAAfA,YAAe,CAACC,OAAD,EAAUC,OAAV,EAAsB;AACzCJ,eAAaK,MAAb,CAAoBF,OAApB;AACAH,eAAaM,GAAb,CAAiBH,OAAjB,EAA0BC,OAA1B;AACA,SAAOJ,aAAaO,IAAb,GAAoBR,mBAA3B,EAAgD;AAC9C,QAAMS,YAAYR,aAAaS,IAAb,GAAoBC,IAApB,GAA2BC,KAA7C;AACAX,iBAAaK,MAAb,CAAoBG,SAApB;AACD;AACD,SAAOJ,OAAP;AACD,CARD;;AAUA,IAAMQ,aAAa,SAAbA,UAAa,UAAW;AAC5BC,OAAKC,WAAL,CAAiBC,OAAjB;AACD,CAFD;;AAIA,IAAMC,iBAAiB,SAAjBA,cAAiB;AAAA,SAASC,OAAQC,SAASA,MAAMH,OAAhB,IAA4BG,KAAnC,CAAT;AAAA,CAAvB;;AAEAL,KAAKM,gBAAL,CAAsB,SAAtB,EAAiC,iBAAS;AACxC,MAAMJ,UAAUK,MAAMC,IAAN,IAAc,EAA9B;;AAEA,MAAIN,QAAQO,IAAR,KAAiB,eAArB,EAAsC;AACpC,QAAI;AACFpB,mBACEa,QAAQZ,OADV,EAEE,mCAAgBY,QAAQM,IAAxB,EAA8B,EAA9B,EAAkC,UAACE,SAAD,EAAYC,KAAZ,EAAsB;AACtDZ,mBAAW;AACTU,gBAAM,gBADG;AAETG,cAAIV,QAAQU,EAFH;AAGTF,8BAHS;AAITC,sBAJS;AAKTE,iBAAO;AALE,SAAX;AAOD,OARD,CAFF;AAYD,KAbD,CAaE,OAAOR,KAAP,EAAc;AACdN,iBAAW;AACTU,cAAM,aADG;AAETG,YAAIV,QAAQU,EAFH;AAGTV,iBAASC,eAAeE,KAAf;AAHA,OAAX;AAKD;AACD;AACD;;AAED,MAAIH,QAAQO,IAAR,KAAiB,eAArB,EAAsC;AACpC,QAAI;AACF,UAAMlB,UAAUJ,aAAa2B,GAAb,CAAiBZ,QAAQZ,OAAzB,CAAhB;AACA,UAAI,CAACC,OAAL,EAAc;AACZ,cAAM,IAAIwB,KAAJ,gCAAuCb,QAAQZ,OAA/C,CAAN;AACD;AACD,UAAM0B,SAAS,qCACbzB,OADa,EAEbW,QAAQe,MAFK,EAGb,UAACP,SAAD,EAAYC,KAAZ,EAAmBE,KAAnB,EAA6B;AAC3Bd,mBAAW;AACTU,gBAAM,gBADG;AAETG,cAAIV,QAAQU,EAFH;AAGTF,8BAHS;AAITC,sBAJS;AAKTE;AALS,SAAX;AAOD,OAXY,CAAf;AAaAd,iBAAW,EAACU,MAAM,cAAP,EAAuBG,IAAIV,QAAQU,EAAnC,EAAuCI,cAAvC,EAAX;AACD,KAnBD,CAmBE,OAAOX,KAAP,EAAc;AACdN,iBAAW;AACTU,cAAM,aADG;AAETG,YAAIV,QAAQU,EAFH;AAGTV,iBAASC,eAAeE,KAAf;AAHA,OAAX;AAKD;AACF;AACF,CAvDD","file":"pivotWorker.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/*\n * Web Worker entry point for off-main-thread pivot computation.\n *\n * Message protocol (plain, structured-cloneable objects):\n * main -> worker {type: 'pivot:setData', id, dataKey, data}\n * main -> worker {type: 'pivot:compute', id, dataKey, config}\n * worker -> main {type: 'pivot:progress', id, processed, total, phase}\n * worker -> main {type: 'pivot:result', id, result}\n * worker -> main {type: 'pivot:error', id, message}\n *\n * The materialized dataset is cached per dataKey, so repeated computations on\n * the same data (e.g. dragging fields around) do not pay the cost of\n * re-scanning the raw records.\n */\nimport {materializeData, computePivotState} from './pivotCompute';\n\nconst MAX_CACHED_DATASETS = 4;\n\nconst datasetCache = new Map();\n\nconst cacheDataset = (dataKey, dataset) => {\n datasetCache.delete(dataKey);\n datasetCache.set(dataKey, dataset);\n while (datasetCache.size > MAX_CACHED_DATASETS) {\n const oldestKey = datasetCache.keys().next().value;\n datasetCache.delete(oldestKey);\n }\n return dataset;\n};\n\nconst postToMain = message => {\n self.postMessage(message);\n};\n\nconst toErrorMessage = error => String((error && error.message) || error);\n\nself.addEventListener('message', event => {\n const message = event.data || {};\n\n if (message.type === 'pivot:setData') {\n try {\n cacheDataset(\n message.dataKey,\n materializeData(message.data, {}, (processed, total) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase: 'materialize',\n });\n })\n );\n } catch (error) {\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n return;\n }\n\n if (message.type === 'pivot:compute') {\n try {\n const dataset = datasetCache.get(message.dataKey);\n if (!dataset) {\n throw new Error(`no dataset staged for key ${message.dataKey}`);\n }\n const result = computePivotState(\n dataset,\n message.config,\n (processed, total, phase) => {\n postToMain({\n type: 'pivot:progress',\n id: message.id,\n processed,\n total,\n phase,\n });\n }\n );\n postToMain({type: 'pivot:result', id: message.id, result});\n } catch (error) {\n postToMain({\n type: 'pivot:error',\n id: message.id,\n message: toErrorMessage(error),\n });\n }\n }\n});\n"]}
|
package/dist/pivottable.css
CHANGED
|
@@ -375,3 +375,77 @@ table.pvtTable tbody tr td {
|
|
|
375
375
|
.pvtAxisLabel:hover .pvtSortButton {
|
|
376
376
|
opacity: 1;
|
|
377
377
|
}
|
|
378
|
+
|
|
379
|
+
/* ===== Web Worker loading overlay ===== */
|
|
380
|
+
|
|
381
|
+
.pvtOutput {
|
|
382
|
+
position: relative;
|
|
383
|
+
min-height: 220px;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.pvtLoadingOverlay {
|
|
387
|
+
position: absolute;
|
|
388
|
+
top: 0;
|
|
389
|
+
left: 0;
|
|
390
|
+
right: 0;
|
|
391
|
+
bottom: 0;
|
|
392
|
+
display: flex;
|
|
393
|
+
flex-direction: column;
|
|
394
|
+
align-items: center;
|
|
395
|
+
justify-content: center;
|
|
396
|
+
gap: 10px;
|
|
397
|
+
background: rgba(255, 255, 255, 0.85);
|
|
398
|
+
z-index: 20;
|
|
399
|
+
cursor: wait;
|
|
400
|
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.pvtOutputPlaceholder {
|
|
404
|
+
min-height: 220px;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.pvtLoadingSpinner {
|
|
408
|
+
width: 36px;
|
|
409
|
+
height: 36px;
|
|
410
|
+
border: 4px solid #ffeeee;
|
|
411
|
+
border-top-color: #ff6600;
|
|
412
|
+
border-radius: 50%;
|
|
413
|
+
animation: pvtLoadingSpin 0.9s linear infinite;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@keyframes pvtLoadingSpin {
|
|
417
|
+
to {
|
|
418
|
+
transform: rotate(360deg);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.pvtLoadingText {
|
|
423
|
+
font-size: 14px;
|
|
424
|
+
color: #333;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.pvtLoadingBarWrap {
|
|
428
|
+
width: 240px;
|
|
429
|
+
height: 8px;
|
|
430
|
+
background: #ffeeee;
|
|
431
|
+
border-radius: 4px;
|
|
432
|
+
overflow: hidden;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.pvtLoadingBar {
|
|
436
|
+
height: 100%;
|
|
437
|
+
background: #ff6600;
|
|
438
|
+
transition: width 0.2s ease;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.pvtLoadingPercent {
|
|
442
|
+
font-size: 12px;
|
|
443
|
+
font-weight: 600;
|
|
444
|
+
color: #ff6600;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.pvtComputeError {
|
|
448
|
+
padding: 20px;
|
|
449
|
+
color: #8b0000;
|
|
450
|
+
text-align: center;
|
|
451
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hrforte/react-pivottable",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"dist"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"start": "webpack-dev-server",
|
|
13
|
+
"start": "npm run build:worker && webpack-dev-server",
|
|
14
|
+
"build:worker": "webpack --config webpack.worker.config.js",
|
|
14
15
|
"test:eslint": "eslint src/*.js* && echo 'eslint: \\033[0;32m'PASS'\\033[0m'",
|
|
15
16
|
"test:eslint:fix": "eslint src/*.js* --fix",
|
|
16
17
|
"test:prettier": "prettier -l \"src/*.js*\" && echo 'prettier: \\033[0;32m'PASS'\\033[0m'",
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
"test:jest": "jest",
|
|
19
20
|
"test": "npm run test:eslint && npm run test:prettier && npm run test:jest",
|
|
20
21
|
"clean": "rm -rf __tests__ PivotTable.js* PivotTableUI.js* PlotlyRenderers.js* TableRenderers.js* Utilities.js* pivottable.css",
|
|
21
|
-
"build": "npm run clean && mkdir -p dist && cp src/pivottable.css dist/ && babel src --out-dir=dist --source-maps --presets=env,react --plugins babel-plugin-add-module-exports",
|
|
22
|
+
"build": "npm run clean && mkdir -p dist && cp src/pivottable.css dist/ && babel src --out-dir=dist --source-maps --presets=env,react --plugins babel-plugin-add-module-exports && PIVOT_WORKER_OUT=dist npm run build:worker",
|
|
22
23
|
"doPublish": "npm run build && npm publish",
|
|
23
24
|
"postpublish": "npm run clean",
|
|
24
25
|
"deploy": "webpack -p && mv bundle.js examples && cd examples && git init && git add . && git commit -m build && git push --force git@github.com:plotly/react-pivottable.git master:gh-pages && rm -rf .git bundle.js"
|