@hrforte/react-pivottable 2.1.2 → 2.2.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 +5 -3
- package/dist/PivotDataWorker.js +11 -16
- package/dist/PivotDataWorker.js.map +1 -1
- package/dist/PivotTableUI.js +59 -70
- package/dist/PivotTableUI.js.map +1 -1
- package/dist/PlotlyRenderers.js +9 -5
- package/dist/PlotlyRenderers.js.map +1 -1
- package/dist/TableRenderers.js +0 -1
- package/dist/TableRenderers.js.map +1 -1
- package/dist/Utilities.js +2 -20
- package/dist/Utilities.js.map +1 -1
- package/dist/__tests__/PivotDataWorker-test.js +7 -8
- package/dist/__tests__/PivotDataWorker-test.js.map +1 -1
- package/dist/__tests__/PivotTableUI-processing-test.js +104 -9
- package/dist/__tests__/PivotTableUI-processing-test.js.map +1 -1
- package/dist/__tests__/pivotCompute-test.js +7 -7
- package/dist/__tests__/pivotCompute-test.js.map +1 -1
- package/dist/pivot-worker.js +8 -60
- package/dist/pivotCompute.js +5 -22
- package/dist/pivotCompute.js.map +1 -1
- package/dist/pivotWorker.js +2 -18
- package/dist/pivotWorker.js.map +1 -1
- package/dist/pivottable.css +0 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,7 +122,7 @@ but the main thread is used. Verify with `workerDebug`:
|
|
|
122
122
|
- `workerUrl` (`string`, default `'pivot-worker.js'`): URL of the worker bundle
|
|
123
123
|
(see above).
|
|
124
124
|
- `workerDebug` (`bool`, default `false`): prints the whole worker pipeline
|
|
125
|
-
(worker creation, data staging,
|
|
125
|
+
(worker creation, data staging, compute, result, errors and the
|
|
126
126
|
fallback to the main thread) to the browser console with a
|
|
127
127
|
`[pivottable worker]` prefix. Logs emitted inside the worker thread are
|
|
128
128
|
forwarded to the page console as well. Can also be enabled globally with
|
|
@@ -130,5 +130,7 @@ but the main thread is used. Verify with `workerDebug`:
|
|
|
130
130
|
- `isShowProcessing` (`bool`, default `false`): testing/debugging hook. When
|
|
131
131
|
`true` the loading overlay is forced on top of the table even if nothing is
|
|
132
132
|
being computed, so the `PivotLoadingOverlay` can be inspected without
|
|
133
|
-
generating a large dataset.
|
|
134
|
-
|
|
133
|
+
generating a large dataset. The overlay shows only a spinner and a label;
|
|
134
|
+
during real computations it is turned off only after the worker has posted
|
|
135
|
+
its result event **and** PivotTableUI has rendered the new table (the
|
|
136
|
+
removal happens before the browser paints, so no empty frame is shown).
|
package/dist/PivotDataWorker.js
CHANGED
|
@@ -20,7 +20,7 @@ var DEFAULT_WORKER_URL = exports.DEFAULT_WORKER_URL = 'pivot-worker.js'; /* esli
|
|
|
20
20
|
*
|
|
21
21
|
* Responsibilities:
|
|
22
22
|
* - lazily create a single shared Worker (at `workerUrl`, overrideable),
|
|
23
|
-
* - route
|
|
23
|
+
* - route result/error messages to the matching request,
|
|
24
24
|
* - transparently fall back to a synchronous main-thread computation when
|
|
25
25
|
* workers are unavailable or the worker bundle fails to load.
|
|
26
26
|
*
|
|
@@ -126,11 +126,9 @@ var handleWorkerMessage = function handleWorkerMessage(event) {
|
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
if (message.type === 'pivot:progress') {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
entry.onProgress(message.processed, message.total, message.phase);
|
|
133
|
-
}
|
|
129
|
+
// Progress reporting was removed: the UI only shows a loading overlay
|
|
130
|
+
// until `pivot:result` arrives. Ignore messages from older worker
|
|
131
|
+
// bundles that still emit progress so the request is not corrupted.
|
|
134
132
|
return;
|
|
135
133
|
}
|
|
136
134
|
clearTimeout(entry.timer);
|
|
@@ -193,7 +191,7 @@ var ensureWorker = function ensureWorker() {
|
|
|
193
191
|
* worker reuse the materialized dataset across consecutive computations and
|
|
194
192
|
* lets the client skip re-staging identical data.
|
|
195
193
|
*/
|
|
196
|
-
function computePivotInWorker(data, dataKey, config
|
|
194
|
+
function computePivotInWorker(data, dataKey, config) {
|
|
197
195
|
var worker = ensureWorker();
|
|
198
196
|
if (!worker) {
|
|
199
197
|
log('worker unavailable -> synchronous fallback');
|
|
@@ -209,7 +207,6 @@ function computePivotInWorker(data, dataKey, config, onProgress) {
|
|
|
209
207
|
pendingRequests.set(id, {
|
|
210
208
|
resolve: resolve,
|
|
211
209
|
reject: reject,
|
|
212
|
-
onProgress: onProgress,
|
|
213
210
|
timer: timer,
|
|
214
211
|
startedAt: Date.now()
|
|
215
212
|
});
|
|
@@ -228,16 +225,14 @@ function computePivotInWorker(data, dataKey, config, onProgress) {
|
|
|
228
225
|
* automatic fallback whenever the worker path is unavailable. Deferred by
|
|
229
226
|
* one tick so a loading indicator can paint before the blocking work starts.
|
|
230
227
|
*/
|
|
231
|
-
function computePivotSynchronously(data, config
|
|
228
|
+
function computePivotSynchronously(data, config) {
|
|
232
229
|
return new Promise(function (resolve, reject) {
|
|
233
230
|
setTimeout(function () {
|
|
234
231
|
var startedAt = Date.now();
|
|
235
232
|
log('running synchronous computation on the main thread...');
|
|
236
233
|
try {
|
|
237
|
-
var materialized = (0, _pivotCompute.materializeData)(data, {}
|
|
238
|
-
|
|
239
|
-
});
|
|
240
|
-
var result = (0, _pivotCompute.computePivotState)(materialized, config, onProgress);
|
|
234
|
+
var materialized = (0, _pivotCompute.materializeData)(data, {});
|
|
235
|
+
var result = (0, _pivotCompute.computePivotState)(materialized, config);
|
|
241
236
|
log('synchronous computation finished in ' + (Date.now() - startedAt) + 'ms', {
|
|
242
237
|
totalRecords: result.totalRecords,
|
|
243
238
|
rowKeys: result.rowKeys.length,
|
|
@@ -257,10 +252,10 @@ function computePivotSynchronously(data, config, onProgress) {
|
|
|
257
252
|
* produces the exact same serialized shape, so callers render identically
|
|
258
253
|
* either way.
|
|
259
254
|
*/
|
|
260
|
-
function computePivotWithFallback(data, dataKey, config
|
|
261
|
-
return computePivotInWorker(data, dataKey, config
|
|
255
|
+
function computePivotWithFallback(data, dataKey, config) {
|
|
256
|
+
return computePivotInWorker(data, dataKey, config).catch(function (error) {
|
|
262
257
|
log('worker request failed, falling back to the main thread:', error && error.message || error);
|
|
263
|
-
return computePivotSynchronously(data, config
|
|
258
|
+
return computePivotSynchronously(data, config);
|
|
264
259
|
});
|
|
265
260
|
}
|
|
266
261
|
//# sourceMappingURL=PivotDataWorker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/PivotDataWorker.js"],"names":["setWorkerDebug","configureWorkerUrl","computePivotInWorker","computePivotSynchronously","computePivotWithFallback","DEFAULT_WORKER_URL","REQUEST_TIMEOUT_MS","workerUrl","sharedWorker","sharedWorkerBroken","hasResultEver","nextRequestId","lastStagedDataKey","workerDebug","pendingRequests","Map","enabled","next","Boolean","wasEnabled","log","postMessage","type","isDebugEnabled","window","__PVT_WORKER_DEBUG__","args","describeConfig","config","rows","join","cols","aggregations","length","countOf","Array","isArray","value","undefined","failAllPending","forEach","clearTimeout","entry","timer","reject","error","clear","url","Error","terminate","handleWorkerMessage","message","event","data","detail","get","id","percent","total","Math","round","processed","phase","onProgress","delete","result","Date","now","startedAt","totalRecords","rowKeys","colKeys","resolve","ensureWorker","Worker","onmessage","onerror","dataKey","worker","Promise","setTimeout","set","mustStage","materialized","catch"],"mappings":"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","file":"PivotDataWorker.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/* eslint-disable no-console */\n/* eslint-disable no-undefined */\n/*\n * Main-thread client for the pivot Web Worker (src/pivotWorker.js).\n *\n * Responsibilities:\n * - lazily create a single shared Worker (at `workerUrl`, overrideable),\n * - route progress/result/error messages to the matching request,\n * - transparently fall back to a synchronous main-thread computation when\n * workers are unavailable or the worker bundle fails to load.\n *\n * Debug logging is off by default. Turn it on with the `workerDebug` prop of\n * PivotTableUI (or by setting `window.__PVT_WORKER_DEBUG__ = true` before\n * rendering) to trace worker creation, staging, computation, progress,\n * results, errors and fallbacks in the browser console.\n */\nimport {materializeData, computePivotState} from './pivotCompute';\n\nexport const DEFAULT_WORKER_URL = 'pivot-worker.js';\n\nconst REQUEST_TIMEOUT_MS = 300000;\n\nlet workerUrl = DEFAULT_WORKER_URL;\nlet sharedWorker = null;\n// The worker is marked broken only if it fails before ever producing a\n// result (e.g. a 404 worker script); runtime hiccups later on just reject\n// the affected request and fall back once.\nlet sharedWorkerBroken = false;\nlet hasResultEver = false;\nlet nextRequestId = 1;\nlet lastStagedDataKey = null;\nlet workerDebug = false;\nconst pendingRequests = new Map();\n\nexport function setWorkerDebug(enabled) {\n const next = Boolean(enabled);\n const wasEnabled = workerDebug;\n if (next === wasEnabled) {\n return;\n }\n if (next) {\n workerDebug = true;\n log('debug logging enabled');\n } else {\n // Log while logging is still active, so the \"disabled\" notice is shown.\n log('debug logging disabled');\n workerDebug = false;\n }\n if (sharedWorker) {\n sharedWorker.postMessage({type: 'pivot:debug', enabled: workerDebug});\n }\n}\n\nconst isDebugEnabled = () =>\n workerDebug ||\n (typeof window !== 'undefined' && window.__PVT_WORKER_DEBUG__ === true);\n\nfunction log(...args) {\n if (!isDebugEnabled()) {\n return;\n }\n console.log(\n '%c[pivottable worker]',\n 'color:#ff6600;font-weight:bold',\n ...args\n );\n}\n\nconst describeConfig = config =>\n `rows=[${(config.rows || []).join(', ')}] cols=[${(config.cols || []).join(\n ', '\n )}] aggregations=${(config.aggregations || []).length}`;\n\n// Length of an optional array field, used for defensive debug logging.\nconst countOf = value => (Array.isArray(value) ? value.length : undefined);\n\nconst failAllPending = error => {\n pendingRequests.forEach(entry => {\n clearTimeout(entry.timer);\n entry.reject(error);\n });\n pendingRequests.clear();\n};\n\nexport function configureWorkerUrl(url) {\n if (url && url !== workerUrl) {\n workerUrl = url;\n if (sharedWorker) {\n // Reject in-flight requests first so callers fall back to the\n // synchronous path instead of waiting for the request timeout.\n failAllPending(new Error('Pivot worker was reconfigured'));\n sharedWorker.terminate();\n sharedWorker = null;\n sharedWorkerBroken = false;\n hasResultEver = false;\n lastStagedDataKey = null;\n }\n }\n}\n\nconst handleWorkerMessage = event => {\n const message = event.data || {};\n if (message.type === 'pivot:log') {\n log('(in worker)', message.message, message.detail || '');\n return;\n }\n const entry = pendingRequests.get(message.id);\n if (!entry) {\n return;\n }\n if (message.type === 'pivot:progress') {\n const percent =\n message.total > 0\n ? Math.round((message.processed / message.total) * 100)\n : null;\n log(\n `#${message.id} progress ${message.phase}: ${message.processed}` +\n (message.total ? `/${message.total}` : '') +\n (percent === null ? '' : ` (${percent}%)`)\n );\n if (entry.onProgress) {\n entry.onProgress(message.processed, message.total, message.phase);\n }\n return;\n }\n clearTimeout(entry.timer);\n pendingRequests.delete(message.id);\n if (message.type === 'pivot:result') {\n hasResultEver = true;\n const result = message.result;\n log(`#${message.id} result received in ${Date.now() - entry.startedAt}ms`, {\n totalRecords: result && result.totalRecords,\n rowKeys: countOf(result && result.rowKeys),\n colKeys: countOf(result && result.colKeys),\n aggregations: countOf(result && result.aggregations),\n });\n entry.resolve(message.result);\n } else if (message.type === 'pivot:error') {\n log(`#${message.id} worker reported an error:`, message.message);\n entry.reject(new Error(message.message || 'Pivot worker error'));\n }\n};\n\nconst ensureWorker = () => {\n if (sharedWorkerBroken) {\n return null;\n }\n\n if (typeof Worker === 'undefined') {\n log('no Worker support in this environment');\n return null;\n }\n\n if (!sharedWorker) {\n try {\n sharedWorker = new Worker(workerUrl);\n log('worker created:', workerUrl);\n } catch (error) {\n sharedWorkerBroken = true;\n log('worker creation failed:', error && error.message);\n return null;\n }\n sharedWorker.onmessage = handleWorkerMessage;\n sharedWorker.onerror = event => {\n log(\n 'worker error event (script failed to load / runtime error):',\n (event && event.message) || ''\n );\n failAllPending(new Error('Pivot worker failed to load'));\n if (!hasResultEver) {\n sharedWorkerBroken = true;\n log('worker marked unusable -> will use the synchronous fallback');\n }\n };\n if (isDebugEnabled()) {\n sharedWorker.postMessage({type: 'pivot:debug', enabled: true});\n }\n }\n return sharedWorker;\n};\n\n/*\n * Runs a full computation inside the shared worker.\n *\n * `dataKey` must change whenever the underlying dataset changes. It lets the\n * worker reuse the materialized dataset across consecutive computations and\n * lets the client skip re-staging identical data.\n */\nexport function computePivotInWorker(data, dataKey, config, onProgress) {\n const worker = ensureWorker();\n if (!worker) {\n log('worker unavailable -> synchronous fallback');\n return Promise.reject(new Error('Web worker unavailable'));\n }\n const id = nextRequestId++;\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(id);\n log(`#${id} timed out after ${REQUEST_TIMEOUT_MS}ms`);\n reject(new Error('Pivot worker timed out'));\n }, REQUEST_TIMEOUT_MS);\n pendingRequests.set(id, {\n resolve,\n reject,\n onProgress,\n timer,\n startedAt: Date.now(),\n });\n const mustStage = lastStagedDataKey !== dataKey;\n log(\n `#${id} dispatching to worker (dataKey=${dataKey}${\n mustStage\n ? ', staging ' + (data || []).length + ' records'\n : ', cached dataset'\n }, ${describeConfig(config)})`\n );\n if (mustStage) {\n worker.postMessage({type: 'pivot:setData', id, dataKey, data});\n lastStagedDataKey = dataKey;\n }\n worker.postMessage({type: 'pivot:compute', id, dataKey, config});\n });\n}\n\n/*\n * Same contract as the worker path, executed on the main thread. Used as the\n * automatic fallback whenever the worker path is unavailable. Deferred by\n * one tick so a loading indicator can paint before the blocking work starts.\n */\nexport function computePivotSynchronously(data, config, onProgress) {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n const startedAt = Date.now();\n log('running synchronous computation on the main thread...');\n try {\n const materialized = materializeData(data, {}, (processed, total) => {\n onProgress(processed, total, 'materialize');\n });\n const result = computePivotState(materialized, config, onProgress);\n log(`synchronous computation finished in ${Date.now() - startedAt}ms`, {\n totalRecords: result.totalRecords,\n rowKeys: result.rowKeys.length,\n colKeys: result.colKeys.length,\n });\n resolve(result);\n } catch (error) {\n log('synchronous computation failed:', error && error.message);\n reject(error);\n }\n }, 0);\n });\n}\n\n/*\n * Worker-first entry point with automatic synchronous fallback. The fallback\n * produces the exact same serialized shape, so callers render identically\n * either way.\n */\nexport function computePivotWithFallback(data, dataKey, config, onProgress) {\n return computePivotInWorker(data, dataKey, config, onProgress).catch(\n error => {\n log(\n 'worker request failed, falling back to the main thread:',\n (error && error.message) || error\n );\n return computePivotSynchronously(data, config, onProgress);\n }\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/PivotDataWorker.js"],"names":["setWorkerDebug","configureWorkerUrl","computePivotInWorker","computePivotSynchronously","computePivotWithFallback","DEFAULT_WORKER_URL","REQUEST_TIMEOUT_MS","workerUrl","sharedWorker","sharedWorkerBroken","hasResultEver","nextRequestId","lastStagedDataKey","workerDebug","pendingRequests","Map","enabled","next","Boolean","wasEnabled","log","postMessage","type","isDebugEnabled","window","__PVT_WORKER_DEBUG__","args","describeConfig","config","rows","join","cols","aggregations","length","countOf","Array","isArray","value","undefined","failAllPending","forEach","clearTimeout","entry","timer","reject","error","clear","url","Error","terminate","handleWorkerMessage","message","event","data","detail","get","id","delete","result","Date","now","startedAt","totalRecords","rowKeys","colKeys","resolve","ensureWorker","Worker","onmessage","onerror","dataKey","worker","Promise","setTimeout","set","mustStage","materialized","catch"],"mappings":"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","file":"PivotDataWorker.js","sourcesContent":["/* eslint-disable valid-jsdoc */\n/* eslint-disable no-console */\n/* eslint-disable no-undefined */\n/*\n * Main-thread client for the pivot Web Worker (src/pivotWorker.js).\n *\n * Responsibilities:\n * - lazily create a single shared Worker (at `workerUrl`, overrideable),\n * - route result/error messages to the matching request,\n * - transparently fall back to a synchronous main-thread computation when\n * workers are unavailable or the worker bundle fails to load.\n *\n * Debug logging is off by default. Turn it on with the `workerDebug` prop of\n * PivotTableUI (or by setting `window.__PVT_WORKER_DEBUG__ = true` before\n * rendering) to trace worker creation, staging, computation, progress,\n * results, errors and fallbacks in the browser console.\n */\nimport {materializeData, computePivotState} from './pivotCompute';\n\nexport const DEFAULT_WORKER_URL = 'pivot-worker.js';\n\nconst REQUEST_TIMEOUT_MS = 300000;\n\nlet workerUrl = DEFAULT_WORKER_URL;\nlet sharedWorker = null;\n// The worker is marked broken only if it fails before ever producing a\n// result (e.g. a 404 worker script); runtime hiccups later on just reject\n// the affected request and fall back once.\nlet sharedWorkerBroken = false;\nlet hasResultEver = false;\nlet nextRequestId = 1;\nlet lastStagedDataKey = null;\nlet workerDebug = false;\nconst pendingRequests = new Map();\n\nexport function setWorkerDebug(enabled) {\n const next = Boolean(enabled);\n const wasEnabled = workerDebug;\n if (next === wasEnabled) {\n return;\n }\n if (next) {\n workerDebug = true;\n log('debug logging enabled');\n } else {\n // Log while logging is still active, so the \"disabled\" notice is shown.\n log('debug logging disabled');\n workerDebug = false;\n }\n if (sharedWorker) {\n sharedWorker.postMessage({type: 'pivot:debug', enabled: workerDebug});\n }\n}\n\nconst isDebugEnabled = () =>\n workerDebug ||\n (typeof window !== 'undefined' && window.__PVT_WORKER_DEBUG__ === true);\n\nfunction log(...args) {\n if (!isDebugEnabled()) {\n return;\n }\n console.log(\n '%c[pivottable worker]',\n 'color:#ff6600;font-weight:bold',\n ...args\n );\n}\n\nconst describeConfig = config =>\n `rows=[${(config.rows || []).join(', ')}] cols=[${(config.cols || []).join(\n ', '\n )}] aggregations=${(config.aggregations || []).length}`;\n\n// Length of an optional array field, used for defensive debug logging.\nconst countOf = value => (Array.isArray(value) ? value.length : undefined);\n\nconst failAllPending = error => {\n pendingRequests.forEach(entry => {\n clearTimeout(entry.timer);\n entry.reject(error);\n });\n pendingRequests.clear();\n};\n\nexport function configureWorkerUrl(url) {\n if (url && url !== workerUrl) {\n workerUrl = url;\n if (sharedWorker) {\n // Reject in-flight requests first so callers fall back to the\n // synchronous path instead of waiting for the request timeout.\n failAllPending(new Error('Pivot worker was reconfigured'));\n sharedWorker.terminate();\n sharedWorker = null;\n sharedWorkerBroken = false;\n hasResultEver = false;\n lastStagedDataKey = null;\n }\n }\n}\n\nconst handleWorkerMessage = event => {\n const message = event.data || {};\n if (message.type === 'pivot:log') {\n log('(in worker)', message.message, message.detail || '');\n return;\n }\n const entry = pendingRequests.get(message.id);\n if (!entry) {\n return;\n }\n if (message.type === 'pivot:progress') {\n // Progress reporting was removed: the UI only shows a loading overlay\n // until `pivot:result` arrives. Ignore messages from older worker\n // bundles that still emit progress so the request is not corrupted.\n return;\n }\n clearTimeout(entry.timer);\n pendingRequests.delete(message.id);\n if (message.type === 'pivot:result') {\n hasResultEver = true;\n const result = message.result;\n log(`#${message.id} result received in ${Date.now() - entry.startedAt}ms`, {\n totalRecords: result && result.totalRecords,\n rowKeys: countOf(result && result.rowKeys),\n colKeys: countOf(result && result.colKeys),\n aggregations: countOf(result && result.aggregations),\n });\n entry.resolve(message.result);\n } else if (message.type === 'pivot:error') {\n log(`#${message.id} worker reported an error:`, message.message);\n entry.reject(new Error(message.message || 'Pivot worker error'));\n }\n};\n\nconst ensureWorker = () => {\n if (sharedWorkerBroken) {\n return null;\n }\n\n if (typeof Worker === 'undefined') {\n log('no Worker support in this environment');\n return null;\n }\n\n if (!sharedWorker) {\n try {\n sharedWorker = new Worker(workerUrl);\n log('worker created:', workerUrl);\n } catch (error) {\n sharedWorkerBroken = true;\n log('worker creation failed:', error && error.message);\n return null;\n }\n sharedWorker.onmessage = handleWorkerMessage;\n sharedWorker.onerror = event => {\n log(\n 'worker error event (script failed to load / runtime error):',\n (event && event.message) || ''\n );\n failAllPending(new Error('Pivot worker failed to load'));\n if (!hasResultEver) {\n sharedWorkerBroken = true;\n log('worker marked unusable -> will use the synchronous fallback');\n }\n };\n if (isDebugEnabled()) {\n sharedWorker.postMessage({type: 'pivot:debug', enabled: true});\n }\n }\n return sharedWorker;\n};\n\n/*\n * Runs a full computation inside the shared worker.\n *\n * `dataKey` must change whenever the underlying dataset changes. It lets the\n * worker reuse the materialized dataset across consecutive computations and\n * lets the client skip re-staging identical data.\n */\nexport function computePivotInWorker(data, dataKey, config) {\n const worker = ensureWorker();\n if (!worker) {\n log('worker unavailable -> synchronous fallback');\n return Promise.reject(new Error('Web worker unavailable'));\n }\n const id = nextRequestId++;\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingRequests.delete(id);\n log(`#${id} timed out after ${REQUEST_TIMEOUT_MS}ms`);\n reject(new Error('Pivot worker timed out'));\n }, REQUEST_TIMEOUT_MS);\n pendingRequests.set(id, {\n resolve,\n reject,\n timer,\n startedAt: Date.now(),\n });\n const mustStage = lastStagedDataKey !== dataKey;\n log(\n `#${id} dispatching to worker (dataKey=${dataKey}${\n mustStage\n ? ', staging ' + (data || []).length + ' records'\n : ', cached dataset'\n }, ${describeConfig(config)})`\n );\n if (mustStage) {\n worker.postMessage({type: 'pivot:setData', id, dataKey, data});\n lastStagedDataKey = dataKey;\n }\n worker.postMessage({type: 'pivot:compute', id, dataKey, config});\n });\n}\n\n/*\n * Same contract as the worker path, executed on the main thread. Used as the\n * automatic fallback whenever the worker path is unavailable. Deferred by\n * one tick so a loading indicator can paint before the blocking work starts.\n */\nexport function computePivotSynchronously(data, config) {\n return new Promise((resolve, reject) => {\n setTimeout(() => {\n const startedAt = Date.now();\n log('running synchronous computation on the main thread...');\n try {\n const materialized = materializeData(data, {});\n const result = computePivotState(materialized, config);\n log(`synchronous computation finished in ${Date.now() - startedAt}ms`, {\n totalRecords: result.totalRecords,\n rowKeys: result.rowKeys.length,\n colKeys: result.colKeys.length,\n });\n resolve(result);\n } catch (error) {\n log('synchronous computation failed:', error && error.message);\n reject(error);\n }\n }, 0);\n });\n}\n\n/*\n * Worker-first entry point with automatic synchronous fallback. The fallback\n * produces the exact same serialized shape, so callers render identically\n * either way.\n */\nexport function computePivotWithFallback(data, dataKey, config) {\n return computePivotInWorker(data, dataKey, config).catch(error => {\n log(\n 'worker request failed, falling back to the main thread:',\n (error && error.message) || error\n );\n return computePivotSynchronously(data, config);\n });\n}\n"]}
|
package/dist/PivotTableUI.js
CHANGED
|
@@ -1114,14 +1114,12 @@ CellFormattingUI.defaultProps = {
|
|
|
1114
1114
|
// instances share the same Web Worker.
|
|
1115
1115
|
var pivotWorkerInstanceCounter = 0;
|
|
1116
1116
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
};
|
|
1124
|
-
var phaseLabel = phaseLabels[progress.phase] || 'Processing';
|
|
1117
|
+
// Loading overlay shown while a worker computation is running. It is removed
|
|
1118
|
+
// only when both conditions hold: the worker posted its `pivot:result` event
|
|
1119
|
+
// AND PivotTableUI has committed the freshly computed table to the DOM (see
|
|
1120
|
+
// the resultReady/useLayoutEffect handshake below), so content never appears
|
|
1121
|
+
// after the overlay disappears.
|
|
1122
|
+
function PivotLoadingOverlay() {
|
|
1125
1123
|
return _react2.default.createElement(
|
|
1126
1124
|
'div',
|
|
1127
1125
|
{ className: 'pvtLoadingOverlay', role: 'status' },
|
|
@@ -1129,27 +1127,11 @@ function PivotLoadingOverlay(props) {
|
|
|
1129
1127
|
_react2.default.createElement(
|
|
1130
1128
|
'div',
|
|
1131
1129
|
{ className: 'pvtLoadingText' },
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
|
1130
|
+
'Processing\u2026'
|
|
1131
|
+
)
|
|
1146
1132
|
);
|
|
1147
1133
|
}
|
|
1148
1134
|
|
|
1149
|
-
PivotLoadingOverlay.propTypes = {
|
|
1150
|
-
progress: _propTypes2.default.object
|
|
1151
|
-
};
|
|
1152
|
-
|
|
1153
1135
|
function PivotTableUI(props) {
|
|
1154
1136
|
var _useState9 = (0, _react.useState)([]),
|
|
1155
1137
|
_useState10 = _slicedToArray(_useState9, 2),
|
|
@@ -1200,11 +1182,15 @@ function PivotTableUI(props) {
|
|
|
1200
1182
|
_useState22 = _slicedToArray(_useState21, 2),
|
|
1201
1183
|
isProcessing = _useState22[0],
|
|
1202
1184
|
setIsProcessing = _useState22[1];
|
|
1185
|
+
// True once the worker result has been applied but the overlay is still
|
|
1186
|
+
// showing: the fresh table renders underneath first, then a layout effect
|
|
1187
|
+
// clears `isProcessing` before the browser paints.
|
|
1203
1188
|
|
|
1204
|
-
|
|
1189
|
+
|
|
1190
|
+
var _useState23 = (0, _react.useState)(false),
|
|
1205
1191
|
_useState24 = _slicedToArray(_useState23, 2),
|
|
1206
|
-
|
|
1207
|
-
|
|
1192
|
+
resultReady = _useState24[0],
|
|
1193
|
+
setResultReady = _useState24[1];
|
|
1208
1194
|
|
|
1209
1195
|
var _useState25 = (0, _react.useState)(null),
|
|
1210
1196
|
_useState26 = _slicedToArray(_useState25, 2),
|
|
@@ -1288,6 +1274,17 @@ function PivotTableUI(props) {
|
|
|
1288
1274
|
}
|
|
1289
1275
|
});
|
|
1290
1276
|
|
|
1277
|
+
// The loading overlay may only be turned off once the new result has been
|
|
1278
|
+
// rendered: the worker's `.then` sets `resultReady` (which makes this
|
|
1279
|
+
// render output the fresh table underneath the still-visible overlay), and
|
|
1280
|
+
// this layout effect runs after that DOM commit but before the browser
|
|
1281
|
+
// paints - so the overlay disappears exactly when the table is on screen.
|
|
1282
|
+
(0, _react.useLayoutEffect)(function () {
|
|
1283
|
+
if (resultReady && isProcessing) {
|
|
1284
|
+
setIsProcessing(false);
|
|
1285
|
+
}
|
|
1286
|
+
}, [resultReady, isProcessing]);
|
|
1287
|
+
|
|
1291
1288
|
// Schedule an off-main-thread computation whenever the data or the pivot
|
|
1292
1289
|
// configuration changes. Rapid consecutive changes are debounced and
|
|
1293
1290
|
// stale responses are discarded.
|
|
@@ -1296,11 +1293,6 @@ function PivotTableUI(props) {
|
|
|
1296
1293
|
// result is never applied to the new configuration or dataset.
|
|
1297
1294
|
activeRequestRef.current += 1;
|
|
1298
1295
|
|
|
1299
|
-
if (!workerEligible) {
|
|
1300
|
-
setIsProcessing(false);
|
|
1301
|
-
return undefined;
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
1296
|
if (versionedDataRef.current !== props.data) {
|
|
1305
1297
|
versionedDataRef.current = props.data;
|
|
1306
1298
|
dataVersionRef.current += 1;
|
|
@@ -1314,18 +1306,13 @@ function PivotTableUI(props) {
|
|
|
1314
1306
|
|
|
1315
1307
|
(0, _PivotDataWorker.configureWorkerUrl)(props.workerUrl || _PivotDataWorker.DEFAULT_WORKER_URL);
|
|
1316
1308
|
setIsProcessing(true);
|
|
1309
|
+
setResultReady(false);
|
|
1317
1310
|
setComputeError(null);
|
|
1318
|
-
setProgress({ processed: 0, total: props.data.length, phase: 'queued' });
|
|
1319
1311
|
|
|
1320
1312
|
var timer = setTimeout(function () {
|
|
1321
1313
|
var requestId = activeRequestRef.current;
|
|
1322
1314
|
var dataKey = instanceIdRef.current + '-data-' + dataVersionRef.current;
|
|
1323
|
-
(0, _PivotDataWorker.computePivotWithFallback)(props.data, dataKey, (0, _pivotCompute.createWorkerConfig)(props)
|
|
1324
|
-
if (unmountedRef.current || requestId !== activeRequestRef.current) {
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
setProgress({ processed: processed, total: total, phase: phase });
|
|
1328
|
-
}).then(function (result) {
|
|
1315
|
+
(0, _PivotDataWorker.computePivotWithFallback)(props.data, dataKey, (0, _pivotCompute.createWorkerConfig)(props)).then(function (result) {
|
|
1329
1316
|
if (unmountedRef.current || requestId !== activeRequestRef.current) {
|
|
1330
1317
|
return;
|
|
1331
1318
|
}
|
|
@@ -1334,7 +1321,10 @@ function PivotTableUI(props) {
|
|
|
1334
1321
|
setAttrValues(result.attrValues);
|
|
1335
1322
|
setMaterializedInput(result.materializedInput);
|
|
1336
1323
|
setComputeError(null);
|
|
1337
|
-
|
|
1324
|
+
// Keep the overlay up for one more render: `resultReady` makes the
|
|
1325
|
+
// fresh table render underneath, and the layout effect above turns
|
|
1326
|
+
// `isProcessing` off only after that render has been committed.
|
|
1327
|
+
setResultReady(true);
|
|
1338
1328
|
}).catch(function (error) {
|
|
1339
1329
|
if (unmountedRef.current || requestId !== activeRequestRef.current) {
|
|
1340
1330
|
return;
|
|
@@ -1346,8 +1336,7 @@ function PivotTableUI(props) {
|
|
|
1346
1336
|
return function () {
|
|
1347
1337
|
clearTimeout(timer);
|
|
1348
1338
|
};
|
|
1349
|
-
|
|
1350
|
-
}, [props.data, workerEligible, computeSignature]);
|
|
1339
|
+
}, [props.data, computeSignature]);
|
|
1351
1340
|
|
|
1352
1341
|
var sendPropUpdate = function sendPropUpdate(command) {
|
|
1353
1342
|
props.onChange((0, _immutabilityHelper2.default)(props, command));
|
|
@@ -1835,7 +1824,7 @@ function PivotTableUI(props) {
|
|
|
1835
1824
|
var showProcessing = (isProcessing || props.isShowProcessing) && !computeError;
|
|
1836
1825
|
// While recomputing, re-render the last valid table (old props matched to
|
|
1837
1826
|
// their own precomputed result) so the overlay sits on top of real content.
|
|
1838
|
-
var staleView = showProcessing ? lastGoodViewRef.current : null;
|
|
1827
|
+
var staleView = showProcessing && !resultReady ? lastGoodViewRef.current : null;
|
|
1839
1828
|
|
|
1840
1829
|
var outputContent = void 0;
|
|
1841
1830
|
if (computeError) {
|
|
@@ -1862,7 +1851,7 @@ function PivotTableUI(props) {
|
|
|
1862
1851
|
onColSort: handleColSort,
|
|
1863
1852
|
onRowSort: handleRowSort
|
|
1864
1853
|
}));
|
|
1865
|
-
} else if (showProcessing) {
|
|
1854
|
+
} else if (showProcessing && !resultReady) {
|
|
1866
1855
|
// First computation: no previous table to keep visible yet.
|
|
1867
1856
|
outputContent = _react2.default.createElement('div', { className: 'pvtOutputPlaceholder' });
|
|
1868
1857
|
} else {
|
|
@@ -1876,11 +1865,6 @@ function PivotTableUI(props) {
|
|
|
1876
1865
|
}));
|
|
1877
1866
|
}
|
|
1878
1867
|
|
|
1879
|
-
// With the `isShowProcessing` testing hook active and no real computation
|
|
1880
|
-
// running there is no progress to report, so feed the overlay a sample
|
|
1881
|
-
// value to exercise its progress-bar and percentage branch as well.
|
|
1882
|
-
var overlayProgress = props.isShowProcessing && !isProcessing ? progress || { processed: 60, total: 100, phase: 'aggregate' } : progress;
|
|
1883
|
-
|
|
1884
1868
|
var outputCell = _react2.default.createElement(
|
|
1885
1869
|
'td',
|
|
1886
1870
|
{ className: 'pvtOutput' },
|
|
@@ -1891,7 +1875,7 @@ function PivotTableUI(props) {
|
|
|
1891
1875
|
return _react2.default.createElement(
|
|
1892
1876
|
_react2.default.Fragment,
|
|
1893
1877
|
null,
|
|
1894
|
-
showProcessing ? _react2.default.createElement(PivotLoadingOverlay,
|
|
1878
|
+
showProcessing ? _react2.default.createElement(PivotLoadingOverlay, null) : null,
|
|
1895
1879
|
_react2.default.createElement(
|
|
1896
1880
|
'table',
|
|
1897
1881
|
{ className: 'pvtUi' },
|
|
@@ -1924,26 +1908,31 @@ function PivotTableUI(props) {
|
|
|
1924
1908
|
}
|
|
1925
1909
|
|
|
1926
1910
|
return _react2.default.createElement(
|
|
1927
|
-
|
|
1928
|
-
|
|
1911
|
+
_react2.default.Fragment,
|
|
1912
|
+
null,
|
|
1913
|
+
showProcessing ? _react2.default.createElement(PivotLoadingOverlay, null) : null,
|
|
1929
1914
|
_react2.default.createElement(
|
|
1930
|
-
'
|
|
1931
|
-
{
|
|
1932
|
-
return setOpenDropdown(false);
|
|
1933
|
-
} },
|
|
1934
|
-
_react2.default.createElement(
|
|
1935
|
-
'tr',
|
|
1936
|
-
null,
|
|
1937
|
-
rendererCell,
|
|
1938
|
-
aggregatorCell,
|
|
1939
|
-
colAttrsCell
|
|
1940
|
-
),
|
|
1915
|
+
'table',
|
|
1916
|
+
{ className: 'pvtUi' },
|
|
1941
1917
|
_react2.default.createElement(
|
|
1942
|
-
'
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1918
|
+
'tbody',
|
|
1919
|
+
{ onClick: function onClick() {
|
|
1920
|
+
return setOpenDropdown(false);
|
|
1921
|
+
} },
|
|
1922
|
+
_react2.default.createElement(
|
|
1923
|
+
'tr',
|
|
1924
|
+
null,
|
|
1925
|
+
rendererCell,
|
|
1926
|
+
aggregatorCell,
|
|
1927
|
+
colAttrsCell
|
|
1928
|
+
),
|
|
1929
|
+
_react2.default.createElement(
|
|
1930
|
+
'tr',
|
|
1931
|
+
null,
|
|
1932
|
+
unusedAttrsCell,
|
|
1933
|
+
rowAttrsCell,
|
|
1934
|
+
outputCell
|
|
1935
|
+
)
|
|
1947
1936
|
)
|
|
1948
1937
|
)
|
|
1949
1938
|
);
|