Skip to content

Commit 228ec9e

Browse files
add incremental data loading in table view with 1k add data rows buffer (#41)
1 parent e0b1882 commit 228ec9e

File tree

2 files changed

+153
-54
lines changed

2 files changed

+153
-54
lines changed

src/views/tableView.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ const d3 = require('d3-dsv');
2626
* Defines Table view class for managing state and behaviour of Table webview panels.
2727
*/
2828
export class TableView {
29+
// table view tracking vars
2930
public static currentView: TableView | undefined;
3031
private static _views: Map<string, TableView> = new Map<string, TableView>();
3132

33+
// table view instance vars
3234
private readonly _webviewPanel: WebviewPanel;
3335
private readonly _extensionUri: Uri;
3436
private readonly _documentUri: Uri;
@@ -37,6 +39,15 @@ export class TableView {
3739
private readonly _fileExtension: string;
3840
private _disposables: Disposable[] = [];
3941

42+
// tabular data vars
43+
private _tableData: [] = [];
44+
private _currentDataPage: number = 0;
45+
private _totalRows: number = 0;
46+
47+
// default page data size for data streaming setup
48+
// TODO: move this to tabular data viewer config options later
49+
private readonly _pageDataSize: number = 1000;
50+
4051
/**
4152
* Reveals current table view or creates new table webview panel for tabular data display.
4253
*
@@ -154,12 +165,13 @@ export class TableView {
154165
// process webview messages
155166
this.webviewPanel.webview.onDidReceiveMessage((message: any) => {
156167
const command: string = message.command;
157-
const text = message.text;
158168
switch (command) {
159169
case 'refresh':
160170
// reload data view and config
161171
this.refresh();
162172
break;
173+
case 'addData':
174+
this.addData(message.dataPage);
163175
case 'saveData':
164176
this.saveData(message.data, message.dataFileName, message.dataFileType);
165177
break;
@@ -171,6 +183,11 @@ export class TableView {
171183
* Reloads table view on data save changes or vscode IDE realod.
172184
*/
173185
public async refresh(): Promise<void> {
186+
// clear loaded data info
187+
this._totalRows = 0;
188+
this._currentDataPage = 0;
189+
this._tableData = [];
190+
174191
// load data
175192
workspace.fs.readFile(this._documentUri).then((binaryData: Uint8Array) => {
176193
const textData: string = new TextDecoder().decode(binaryData);
@@ -181,18 +198,44 @@ export class TableView {
181198
let tableData: any = dsvParser.parse(textData, d3.autoType);
182199
this.logTableData(tableData);
183200

184-
// update webview
201+
// save table data for data streaming later
202+
this._tableData = tableData;
203+
this._totalRows = this._tableData.length;
204+
205+
// send initial data rows to table view for display
206+
const initialDataRows: Array<any> = tableData.slice(0, Math.min(this._pageDataSize, this._totalRows));
185207
this.webviewPanel.webview.postMessage({
186208
command: 'refresh',
187209
fileName: this._fileName,
188210
documentUrl: this._documentUri.toString(),
189-
tableData: tableData
190-
});
211+
tableData: initialDataRows,
212+
totalRows: this._totalRows
213+
});
191214
}, reason => {
192215
window.showErrorMessage(`Could not load \`${this._documentUri}\` content. Reason: \n ${reason}`);
193216
});
194217
}
195218

219+
/**
220+
* Sends more data rows to the table view for incremental data loading and display.
221+
*
222+
* @param dataPage Requested data page index for loading the next set of data rows.
223+
*/
224+
public async addData(dataPage: number): Promise<void> {
225+
const nextRows: number = dataPage * this._pageDataSize;
226+
if (nextRows < this._totalRows) {
227+
// get the next set of data rows to load in table view
228+
const dataRows: Array<any> =
229+
this._tableData.slice(nextRows, Math.min(nextRows + this._pageDataSize, this._totalRows));
230+
231+
// send the next set of data rows to display
232+
this.webviewPanel.webview.postMessage({
233+
command: 'addData',
234+
dataRows: dataRows,
235+
});
236+
}
237+
}
238+
196239
/**
197240
* Saves table data in a new data file.
198241
*/

web/scripts/tableView.js

Lines changed: 106 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ let saveDataFileName = '';
1212
let tableContainer, table, progressRing, saveFileTypeSelector;
1313
let tableColumns = [];
1414
let tableData = [];
15+
let loadedRows = 0;
16+
let totalRows = 0;
17+
let dataPage = 0;
1518

1619
// table view settings
1720
const toolbarHeight = 40; // table view toolbar height offset
@@ -89,8 +92,12 @@ window.addEventListener('message', event => {
8992
fileName = event.data.fileName;
9093
vscode.setState({ documentUrl: documentUrl });
9194
tableData = event.data.tableData;
95+
totalRows = event.data.totalRows;
9296
loadData(tableData, fileName);
9397
break;
98+
case 'addData':
99+
addData(table, event.data.dataRows);
100+
break;
94101
}
95102
});
96103

@@ -140,10 +147,26 @@ function reloadData() {
140147
* Loads and displays table data.
141148
*
142149
* @param {*} tableData Data array to display in tabulator table.
143-
* @param {*} fileName Data file name for table config persistence and reload.
144150
*/
145-
function loadData(tableData, documentUrl) {
151+
function loadData(tableData) {
146152
logTableData(tableData);
153+
if (table === undefined) {
154+
createTable(tableData);
155+
}
156+
else {
157+
// reload table data
158+
clearTable(table);
159+
addData(table, tableData);
160+
progressRing.style.visibility = 'hidden';
161+
}
162+
}
163+
164+
/**
165+
* Creates new Tabulator table with initial set of data to display.
166+
*
167+
* @param {*} tableData Data array to display in tabulator table.
168+
*/
169+
function createTable(tableData) {
147170
if (table === undefined) {
148171
table = new Tabulator('#table-container', {
149172
height: window.innerHeight - toolbarHeight,
@@ -211,63 +234,64 @@ function loadData(tableData, documentUrl) {
211234
}
212235
});
213236

214-
table.on('tableBuilt', function () {
215-
// hide data loading progress ring
216-
progressRing.style.visibility = 'hidden';
217-
218-
// get table columns for debug
219-
const columns = table.getColumns();
220-
console.log('tableView.columns:', columns);
221-
222-
// add row selection column
223-
table.addColumn({
224-
formatter: 'rowSelection',
225-
titleFormatter: 'rowSelection',
226-
headerMenu: [], // don't show header context menu
227-
headerSort: false,
228-
download: false // don't include it in data save
229-
}, true) // add as 1st column
230-
.then(function (column) {
231-
// column - the component for the newly created column
232-
// run code after column has been added
233-
})
234-
.catch(function (error) {
235-
// log adding row selection column error for now
236-
console.error('tableView.addRowSelectionCollumn: Error\n', error);
237-
});
238-
});
239-
}
240-
else {
241-
// reload table data
242-
clearTable(table);
243-
addData(table, tableData);
244-
progressRing.style.visibility = 'hidden';
237+
// update table settings after initial data rows load
238+
table.on('tableBuilt', onTableBuilt);
245239
}
246240
}
247241

248242
/**
249-
* Removes all table data.
243+
* Updates Tabulator table after initial set of data rows is loaded.
250244
*/
251-
function clearTable(table) {
252-
if (table) {
253-
table.clearData();
245+
function onTableBuilt () {
246+
// hide data loading progress ring
247+
progressRing.style.visibility = 'hidden';
248+
249+
// get table columns for debug
250+
const columns = table.getColumns();
251+
console.log('tableView.columns:', columns);
252+
253+
// add row selection column
254+
table.addColumn({
255+
formatter: 'rowSelection',
256+
titleFormatter: 'rowSelection',
257+
headerMenu: [], // don't show header context menu
258+
headerSort: false,
259+
download: false // don't include it in data save
260+
}, true) // add as 1st column
261+
.then(function (column) {
262+
// column - the component for the newly created column
263+
// run code after column has been added
264+
})
265+
.catch(function (error) {
266+
// log adding row selection column error for now
267+
console.error('tableView.addRowSelectionCollumn: Error\n', error);
268+
});
269+
270+
// request more data for incremental data loading
271+
loadedRows = table.getRows().length;
272+
if (loadedRows < totalRows) {
273+
dataPage++;
274+
progressRing.style.visibility = 'visible';
275+
vscode.postMessage({
276+
command: 'addData',
277+
dataPage: dataPage
278+
});
254279
}
255280
}
256281

257282
/**
258-
* Scrolls table data display to the first table row.
283+
* Clears displayed table data.
259284
*/
260-
function scrollToFirstRow() {
261-
const rows = table.getRows();
262-
table.scrollToRow(rows[0], 'top', true);
263-
}
285+
function clearTable(table) {
286+
if (table) {
287+
// clear displayed table view
288+
table.clearData();
264289

265-
/**
266-
* Scrolls table data display to the last table row.
267-
*/
268-
function scrollToLastRow() {
269-
const rows = table.getRows();
270-
table.scrollToRow(rows[rows.length-1], 'top', true);
290+
// reset rows and data page counters
291+
loadedRows = 0;
292+
totalRows = 0;
293+
dataPage = 0;
294+
}
271295
}
272296

273297
/**
@@ -279,7 +303,23 @@ function scrollToLastRow() {
279303
function addData(table, tableData) {
280304
if (table && tableData) {
281305
table.addData(tableData, true)
282-
.then(function (rows) { //rows - array of the row components for the rows updated or added
306+
.then(function (rows) { // rows - array of the row components for the rows updated or added
307+
// update loaded rows counter
308+
loadedRows += rows.length;
309+
if (loadedRows < totalRows) {
310+
// request more data to load and display
311+
dataPage++;
312+
progressRing.style.visibility = 'visible';
313+
vscode.postMessage({
314+
command: 'addData',
315+
dataPage: dataPage
316+
});
317+
}
318+
else {
319+
// hide data loading progress ring
320+
progressRing.style.visibility = 'hidden';
321+
console.log('tableView:rowCount:', loadedRows);
322+
}
283323
})
284324
.catch(function (error) {
285325
// handle error updating data
@@ -288,6 +328,22 @@ function addData(table, tableData) {
288328
}
289329
}
290330

331+
/**
332+
* Scrolls table data display to the first table row.
333+
*/
334+
function scrollToFirstRow() {
335+
const rows = table.getRows();
336+
table.scrollToRow(rows[0], 'top', true);
337+
}
338+
339+
/**
340+
* Scrolls table data display to the last table row.
341+
*/
342+
function scrollToLastRow() {
343+
const rows = table.getRows();
344+
table.scrollToRow(rows[rows.length-1], 'top', true);
345+
}
346+
291347
/**
292348
* Saves table data as CSV, TSV, or JSON data array document.
293349
*/
@@ -335,6 +391,6 @@ function saveData() {
335391
* @param tableData Loaded able data.
336392
*/
337393
function logTableData(tableData) {
338-
console.log('tabular.data.view:rowCount:', tableData.length);
394+
console.log('tableView:rowCount:', tableData.length);
339395
console.log('1st 10 rows:', tableData.slice(0, 10));
340396
}

0 commit comments

Comments
 (0)