juxscript 1.1.176 → 1.1.177
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/lib/components/dataframe.d.ts +6 -0
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +126 -3
- package/lib/components/dataframe.ts +154 -3
- package/lib/storage/TabularDriver.d.ts +6 -0
- package/lib/storage/TabularDriver.d.ts.map +1 -1
- package/lib/storage/TabularDriver.js +26 -0
- package/lib/storage/TabularDriver.ts +31 -0
- package/package.json +1 -1
|
@@ -27,6 +27,8 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
27
27
|
private _df;
|
|
28
28
|
private _driver;
|
|
29
29
|
private _table;
|
|
30
|
+
private _tabs;
|
|
31
|
+
private _sheets;
|
|
30
32
|
private _tableOptions;
|
|
31
33
|
private _uploadRef;
|
|
32
34
|
private _storageKey;
|
|
@@ -66,6 +68,10 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
66
68
|
filterable(v: boolean): this;
|
|
67
69
|
paginated(v: boolean): this;
|
|
68
70
|
rowsPerPage(v: number): this;
|
|
71
|
+
/**
|
|
72
|
+
* ✅ NEW: Render multiple Excel sheets as tabs
|
|
73
|
+
*/
|
|
74
|
+
private _renderMultiSheet;
|
|
69
75
|
private _updateStatus;
|
|
70
76
|
private _setDataFrame;
|
|
71
77
|
private _updateTable;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAOnC,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,SAAS,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,aAAa,CAAC,cAAc,CAAC;IACjE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAc;gBAEf,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IA+BtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAC/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAMhD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAwB9B,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IA4CpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI;IAiBnE,UAAU,CAAC,KAAK,GAAE,MAAsB,EAAE,MAAM,GAAE,MAAoC,EAAE,IAAI,GAAE,MAAiB,GAAG,IAAI;IAStH,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3B,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,IAAI;IAQ7C,MAAM,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAI7E,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI;IAI7C,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,GAAG,IAAI;IAIpF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAQxH,IAAI,EAAE,IAAI,SAAS,GAAG,IAAI,CAAqB;IAC/C,IAAI,MAAM,IAAI,aAAa,CAAyB;IACpD,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAAwB;IACjD,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IACtC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IACjC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;IAC/B,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAsC;IACnE,IAAI,OAAO,IAAI,MAAM,EAAE,CAAoC;IAErD,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUhD,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IACzB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC1B,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAM5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA0HzB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,aAAa;IA0BrB,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,gBAAgB;IA+CxB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAMtC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAgFrE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,CAExF"}
|
|
@@ -3,6 +3,7 @@ import { DataFrame } from '../storage/DataFrame.js';
|
|
|
3
3
|
import { TabularDriver } from '../storage/TabularDriver.js';
|
|
4
4
|
import { FileUpload } from './fileupload.js';
|
|
5
5
|
import { Table } from './table.js';
|
|
6
|
+
import { Tabs } from './tabs.js';
|
|
6
7
|
import { renderIcon } from './icons.js';
|
|
7
8
|
const TRIGGER_EVENTS = [];
|
|
8
9
|
const CALLBACK_EVENTS = ['load', 'error', 'transform'];
|
|
@@ -21,6 +22,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
21
22
|
});
|
|
22
23
|
this._df = null;
|
|
23
24
|
this._table = null;
|
|
25
|
+
this._tabs = null; // ✅ NEW: Tabs for multi-sheet Excel
|
|
26
|
+
this._sheets = new Map(); // ✅ NEW: Store all sheets
|
|
24
27
|
this._uploadRef = null;
|
|
25
28
|
this._storageKey = null;
|
|
26
29
|
this._pendingSource = null;
|
|
@@ -83,9 +86,29 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
83
86
|
this.state.loading = true;
|
|
84
87
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
85
88
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
// ✅ FIX: Check if multi-sheet Excel
|
|
90
|
+
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
91
|
+
file.name.toLowerCase().endsWith('.xls');
|
|
92
|
+
if (isExcel) {
|
|
93
|
+
const sheets = await this._driver.streamFileMultiSheet(file);
|
|
94
|
+
const sheetNames = Object.keys(sheets);
|
|
95
|
+
// Store first sheet to IndexedDB
|
|
96
|
+
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
97
|
+
if (sheetNames.length > 1) {
|
|
98
|
+
// Multi-sheet: render tabs
|
|
99
|
+
this._renderMultiSheet(sheets, file.name);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Single sheet: render normally
|
|
103
|
+
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// CSV/TSV: single sheet
|
|
108
|
+
const df = await this._driver.streamFile(file);
|
|
109
|
+
await this._driver.store(file.name, df, { source: file.name });
|
|
110
|
+
this._setDataFrame(df, file.name);
|
|
111
|
+
}
|
|
89
112
|
}
|
|
90
113
|
catch (err) {
|
|
91
114
|
this._triggerCallback('error', err.message, null, this);
|
|
@@ -185,6 +208,106 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
185
208
|
filterable(v) { this._tableOptions.filterable = v; return this; }
|
|
186
209
|
paginated(v) { this._tableOptions.paginated = v; return this; }
|
|
187
210
|
rowsPerPage(v) { this._tableOptions.rowsPerPage = v; return this; }
|
|
211
|
+
/* ═══════════════════════════════════════════════════
|
|
212
|
+
* MULTI-SHEET RENDERING
|
|
213
|
+
* ═══════════════════════════════════════════════════ */
|
|
214
|
+
/**
|
|
215
|
+
* ✅ NEW: Render multiple Excel sheets as tabs
|
|
216
|
+
*/
|
|
217
|
+
_renderMultiSheet(sheets, sourceName) {
|
|
218
|
+
this.state.loading = false;
|
|
219
|
+
this._sheets.clear();
|
|
220
|
+
const wrapper = document.getElementById(this._id);
|
|
221
|
+
if (!wrapper)
|
|
222
|
+
return;
|
|
223
|
+
// Clear existing table if any
|
|
224
|
+
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
225
|
+
if (existingTable)
|
|
226
|
+
existingTable.remove();
|
|
227
|
+
// Store all sheets
|
|
228
|
+
Object.entries(sheets).forEach(([name, df]) => {
|
|
229
|
+
this._sheets.set(name, df);
|
|
230
|
+
});
|
|
231
|
+
// Create tabs
|
|
232
|
+
const sheetNames = Object.keys(sheets);
|
|
233
|
+
const tabs = new Tabs(`${this._id}-tabs`, {
|
|
234
|
+
tabs: sheetNames.map(name => ({
|
|
235
|
+
id: name,
|
|
236
|
+
label: name,
|
|
237
|
+
content: '' // We'll populate after render
|
|
238
|
+
})),
|
|
239
|
+
activeTab: sheetNames[0]
|
|
240
|
+
});
|
|
241
|
+
this._tabs = tabs;
|
|
242
|
+
// Render tabs into wrapper
|
|
243
|
+
const tabsContainer = document.createElement('div');
|
|
244
|
+
tabsContainer.className = 'jux-dataframe-tabs';
|
|
245
|
+
wrapper.appendChild(tabsContainer);
|
|
246
|
+
tabs.render(tabsContainer);
|
|
247
|
+
// Populate each tab with its DataFrame table
|
|
248
|
+
sheetNames.forEach(sheetName => {
|
|
249
|
+
const df = sheets[sheetName];
|
|
250
|
+
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
251
|
+
striped: this._tableOptions.striped,
|
|
252
|
+
hoverable: this._tableOptions.hoverable,
|
|
253
|
+
sortable: this._tableOptions.sortable,
|
|
254
|
+
filterable: false,
|
|
255
|
+
paginated: this._tableOptions.paginated,
|
|
256
|
+
rowsPerPage: this._tableOptions.rowsPerPage
|
|
257
|
+
});
|
|
258
|
+
// ✅ Convert columns to ColumnDef[]
|
|
259
|
+
const columnDefs = df.columns.map(col => ({
|
|
260
|
+
key: col,
|
|
261
|
+
label: col
|
|
262
|
+
}));
|
|
263
|
+
table.columns(columnDefs).rows(df.toRows());
|
|
264
|
+
// Add table to tab
|
|
265
|
+
tabs.addTabContent(sheetName, table);
|
|
266
|
+
// Add filter if enabled
|
|
267
|
+
if (this._tableOptions.filterable) {
|
|
268
|
+
const filterContainer = document.createElement('div');
|
|
269
|
+
filterContainer.className = 'jux-dataframe-filter';
|
|
270
|
+
const input = document.createElement('input');
|
|
271
|
+
input.type = 'text';
|
|
272
|
+
input.placeholder = `Filter ${sheetName}...`;
|
|
273
|
+
input.className = 'jux-input-element jux-dataframe-filter-input';
|
|
274
|
+
const iconEl = renderIcon('search');
|
|
275
|
+
iconEl.style.width = '16px';
|
|
276
|
+
iconEl.style.height = '16px';
|
|
277
|
+
const iconWrap = document.createElement('span');
|
|
278
|
+
iconWrap.className = 'jux-dataframe-filter-icon';
|
|
279
|
+
iconWrap.appendChild(iconEl);
|
|
280
|
+
filterContainer.appendChild(iconWrap);
|
|
281
|
+
filterContainer.appendChild(input);
|
|
282
|
+
input.addEventListener('input', () => {
|
|
283
|
+
const text = input.value.toLowerCase();
|
|
284
|
+
if (!text) {
|
|
285
|
+
table.rows(df.toRows());
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const filtered = df.filter((row) => {
|
|
289
|
+
return Object.values(row).some(v => v !== null && v !== undefined && String(v).toLowerCase().includes(text));
|
|
290
|
+
});
|
|
291
|
+
table.rows(filtered.toRows());
|
|
292
|
+
});
|
|
293
|
+
// Insert filter before table in tab
|
|
294
|
+
const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
|
|
295
|
+
if (tabPanel) {
|
|
296
|
+
const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
|
|
297
|
+
if (tableWrapper) {
|
|
298
|
+
tabPanel.insertBefore(filterContainer, tableWrapper);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
// Update status
|
|
304
|
+
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
305
|
+
this._updateStatus(`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`, 'success');
|
|
306
|
+
// Set first sheet as active DataFrame
|
|
307
|
+
this._df = sheets[sheetNames[0]];
|
|
308
|
+
this._table = tabs._tabsWrapper?.querySelector(`#${this._id}-table-${sheetNames[0]}`);
|
|
309
|
+
this._triggerCallback('load', this._df, null, this);
|
|
310
|
+
}
|
|
188
311
|
/* ═══════════════════════════════════════════════════
|
|
189
312
|
* INTERNAL
|
|
190
313
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -3,6 +3,7 @@ import { DataFrame } from '../storage/DataFrame.js';
|
|
|
3
3
|
import { TabularDriver } from '../storage/TabularDriver.js';
|
|
4
4
|
import { FileUpload } from './fileupload.js';
|
|
5
5
|
import { Table } from './table.js';
|
|
6
|
+
import { Tabs } from './tabs.js';
|
|
6
7
|
import { renderIcon } from './icons.js';
|
|
7
8
|
|
|
8
9
|
const TRIGGER_EVENTS = [] as const;
|
|
@@ -34,6 +35,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
34
35
|
private _df: DataFrame | null = null;
|
|
35
36
|
private _driver: TabularDriver;
|
|
36
37
|
private _table: Table | null = null;
|
|
38
|
+
private _tabs: Tabs | null = null; // ✅ NEW: Tabs for multi-sheet Excel
|
|
39
|
+
private _sheets: Map<string, DataFrame> = new Map(); // ✅ NEW: Store all sheets
|
|
37
40
|
private _tableOptions: {
|
|
38
41
|
striped: boolean;
|
|
39
42
|
hoverable: boolean;
|
|
@@ -119,10 +122,32 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
119
122
|
const file = files[0];
|
|
120
123
|
this.state.loading = true;
|
|
121
124
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
125
|
+
|
|
122
126
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
// ✅ FIX: Check if multi-sheet Excel
|
|
128
|
+
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
129
|
+
file.name.toLowerCase().endsWith('.xls');
|
|
130
|
+
|
|
131
|
+
if (isExcel) {
|
|
132
|
+
const sheets = await this._driver.streamFileMultiSheet(file);
|
|
133
|
+
const sheetNames = Object.keys(sheets);
|
|
134
|
+
|
|
135
|
+
// Store first sheet to IndexedDB
|
|
136
|
+
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
137
|
+
|
|
138
|
+
if (sheetNames.length > 1) {
|
|
139
|
+
// Multi-sheet: render tabs
|
|
140
|
+
this._renderMultiSheet(sheets, file.name);
|
|
141
|
+
} else {
|
|
142
|
+
// Single sheet: render normally
|
|
143
|
+
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// CSV/TSV: single sheet
|
|
147
|
+
const df = await this._driver.streamFile(file);
|
|
148
|
+
await this._driver.store(file.name, df, { source: file.name });
|
|
149
|
+
this._setDataFrame(df, file.name);
|
|
150
|
+
}
|
|
126
151
|
} catch (err: any) {
|
|
127
152
|
this._triggerCallback('error', err.message, null, this);
|
|
128
153
|
this.state.loading = false;
|
|
@@ -232,6 +257,131 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
232
257
|
paginated(v: boolean): this { this._tableOptions.paginated = v; return this; }
|
|
233
258
|
rowsPerPage(v: number): this { this._tableOptions.rowsPerPage = v; return this; }
|
|
234
259
|
|
|
260
|
+
/* ═══════════════════════════════════════════════════
|
|
261
|
+
* MULTI-SHEET RENDERING
|
|
262
|
+
* ═══════════════════════════════════════════════════ */
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* ✅ NEW: Render multiple Excel sheets as tabs
|
|
266
|
+
*/
|
|
267
|
+
private _renderMultiSheet(sheets: Record<string, DataFrame>, sourceName: string): void {
|
|
268
|
+
this.state.loading = false;
|
|
269
|
+
this._sheets.clear();
|
|
270
|
+
|
|
271
|
+
const wrapper = document.getElementById(this._id);
|
|
272
|
+
if (!wrapper) return;
|
|
273
|
+
|
|
274
|
+
// Clear existing table if any
|
|
275
|
+
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
276
|
+
if (existingTable) existingTable.remove();
|
|
277
|
+
|
|
278
|
+
// Store all sheets
|
|
279
|
+
Object.entries(sheets).forEach(([name, df]) => {
|
|
280
|
+
this._sheets.set(name, df);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Create tabs
|
|
284
|
+
const sheetNames = Object.keys(sheets);
|
|
285
|
+
const tabs = new Tabs(`${this._id}-tabs`, {
|
|
286
|
+
tabs: sheetNames.map(name => ({
|
|
287
|
+
id: name,
|
|
288
|
+
label: name,
|
|
289
|
+
content: '' // We'll populate after render
|
|
290
|
+
})),
|
|
291
|
+
activeTab: sheetNames[0]
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
this._tabs = tabs;
|
|
295
|
+
|
|
296
|
+
// Render tabs into wrapper
|
|
297
|
+
const tabsContainer = document.createElement('div');
|
|
298
|
+
tabsContainer.className = 'jux-dataframe-tabs';
|
|
299
|
+
wrapper.appendChild(tabsContainer);
|
|
300
|
+
tabs.render(tabsContainer);
|
|
301
|
+
|
|
302
|
+
// Populate each tab with its DataFrame table
|
|
303
|
+
sheetNames.forEach(sheetName => {
|
|
304
|
+
const df = sheets[sheetName];
|
|
305
|
+
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
306
|
+
striped: this._tableOptions.striped,
|
|
307
|
+
hoverable: this._tableOptions.hoverable,
|
|
308
|
+
sortable: this._tableOptions.sortable,
|
|
309
|
+
filterable: false,
|
|
310
|
+
paginated: this._tableOptions.paginated,
|
|
311
|
+
rowsPerPage: this._tableOptions.rowsPerPage
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// ✅ Convert columns to ColumnDef[]
|
|
315
|
+
const columnDefs = df.columns.map(col => ({
|
|
316
|
+
key: col,
|
|
317
|
+
label: col
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
table.columns(columnDefs).rows(df.toRows());
|
|
321
|
+
|
|
322
|
+
// Add table to tab
|
|
323
|
+
tabs.addTabContent(sheetName, table);
|
|
324
|
+
|
|
325
|
+
// Add filter if enabled
|
|
326
|
+
if (this._tableOptions.filterable) {
|
|
327
|
+
const filterContainer = document.createElement('div');
|
|
328
|
+
filterContainer.className = 'jux-dataframe-filter';
|
|
329
|
+
|
|
330
|
+
const input = document.createElement('input');
|
|
331
|
+
input.type = 'text';
|
|
332
|
+
input.placeholder = `Filter ${sheetName}...`;
|
|
333
|
+
input.className = 'jux-input-element jux-dataframe-filter-input';
|
|
334
|
+
|
|
335
|
+
const iconEl = renderIcon('search');
|
|
336
|
+
iconEl.style.width = '16px';
|
|
337
|
+
iconEl.style.height = '16px';
|
|
338
|
+
|
|
339
|
+
const iconWrap = document.createElement('span');
|
|
340
|
+
iconWrap.className = 'jux-dataframe-filter-icon';
|
|
341
|
+
iconWrap.appendChild(iconEl);
|
|
342
|
+
|
|
343
|
+
filterContainer.appendChild(iconWrap);
|
|
344
|
+
filterContainer.appendChild(input);
|
|
345
|
+
|
|
346
|
+
input.addEventListener('input', () => {
|
|
347
|
+
const text = input.value.toLowerCase();
|
|
348
|
+
if (!text) {
|
|
349
|
+
table.rows(df.toRows());
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const filtered = df.filter((row) => {
|
|
353
|
+
return Object.values(row).some(v =>
|
|
354
|
+
v !== null && v !== undefined && String(v).toLowerCase().includes(text)
|
|
355
|
+
);
|
|
356
|
+
});
|
|
357
|
+
table.rows(filtered.toRows());
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Insert filter before table in tab
|
|
361
|
+
const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
|
|
362
|
+
if (tabPanel) {
|
|
363
|
+
const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
|
|
364
|
+
if (tableWrapper) {
|
|
365
|
+
tabPanel.insertBefore(filterContainer, tableWrapper);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Update status
|
|
372
|
+
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
373
|
+
this._updateStatus(
|
|
374
|
+
`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`,
|
|
375
|
+
'success'
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
// Set first sheet as active DataFrame
|
|
379
|
+
this._df = sheets[sheetNames[0]];
|
|
380
|
+
this._table = (tabs as any)._tabsWrapper?.querySelector(`#${this._id}-table-${sheetNames[0]}`);
|
|
381
|
+
|
|
382
|
+
this._triggerCallback('load', this._df, null, this);
|
|
383
|
+
}
|
|
384
|
+
|
|
235
385
|
/* ═══════════════════════════════════════════════════
|
|
236
386
|
* INTERNAL
|
|
237
387
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -401,6 +551,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
401
551
|
const file = files[0];
|
|
402
552
|
this.state.loading = true;
|
|
403
553
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
554
|
+
|
|
404
555
|
try {
|
|
405
556
|
const df = await this._driver.streamFile(file);
|
|
406
557
|
await this._driver.store(file.name, df, { source: file.name });
|
|
@@ -73,6 +73,12 @@ export declare class TabularDriver {
|
|
|
73
73
|
* Fetch and stream-parse a remote CSV/TSV file
|
|
74
74
|
*/
|
|
75
75
|
fetch(url: string, options?: ParseOptions): Promise<DataFrame>;
|
|
76
|
+
/**
|
|
77
|
+
* ✅ NEW: Stream Excel file and return all sheets
|
|
78
|
+
* @param file - Excel file (.xlsx or .xls)
|
|
79
|
+
* @returns Record<sheetName, DataFrame>
|
|
80
|
+
*/
|
|
81
|
+
streamFileMultiSheet(file: File): Promise<Record<string, DataFrame>>;
|
|
76
82
|
private _splitLines;
|
|
77
83
|
private _parseLine;
|
|
78
84
|
private _autoType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAA4B;gBAE3B,MAAM,GAAE,MAAsB,EAAE,SAAS,GAAE,MAAiB;IAKlE,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IA4BlC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,SAAS;IA6C7D;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;IAoG5E;;OAEG;YACW,UAAU;IAuExB;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBlD;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBzF;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAwBjD;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA4BzD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAqBlH;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -418,6 +418,32 @@ export class TabularDriver {
|
|
|
418
418
|
df = df.select(...selectCols);
|
|
419
419
|
return df;
|
|
420
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* ✅ NEW: Stream Excel file and return all sheets
|
|
423
|
+
* @param file - Excel file (.xlsx or .xls)
|
|
424
|
+
* @returns Record<sheetName, DataFrame>
|
|
425
|
+
*/
|
|
426
|
+
async streamFileMultiSheet(file) {
|
|
427
|
+
// ✅ FIX: Dynamic import XLSX here
|
|
428
|
+
let XLSX;
|
|
429
|
+
try {
|
|
430
|
+
XLSX = await import('xlsx');
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
throw new Error('XLSX support requires the "xlsx" package. Install it with: npm install xlsx');
|
|
434
|
+
}
|
|
435
|
+
const buffer = await file.arrayBuffer();
|
|
436
|
+
const workbook = XLSX.read(buffer, { type: 'array' });
|
|
437
|
+
const sheets = {};
|
|
438
|
+
workbook.SheetNames.forEach((sheetName) => {
|
|
439
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
440
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: null });
|
|
441
|
+
if (jsonData.length > 0) {
|
|
442
|
+
sheets[sheetName] = new DataFrame(jsonData);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
return sheets;
|
|
446
|
+
}
|
|
421
447
|
/* ═══════════════════════════════════════════════════
|
|
422
448
|
* INTERNAL PARSING HELPERS
|
|
423
449
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -522,6 +522,37 @@ export class TabularDriver {
|
|
|
522
522
|
return df;
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
+
/**
|
|
526
|
+
* ✅ NEW: Stream Excel file and return all sheets
|
|
527
|
+
* @param file - Excel file (.xlsx or .xls)
|
|
528
|
+
* @returns Record<sheetName, DataFrame>
|
|
529
|
+
*/
|
|
530
|
+
async streamFileMultiSheet(file: File): Promise<Record<string, DataFrame>> {
|
|
531
|
+
// ✅ FIX: Dynamic import XLSX here
|
|
532
|
+
let XLSX: any;
|
|
533
|
+
try {
|
|
534
|
+
XLSX = await import('xlsx');
|
|
535
|
+
} catch {
|
|
536
|
+
throw new Error('XLSX support requires the "xlsx" package. Install it with: npm install xlsx');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const buffer = await file.arrayBuffer();
|
|
540
|
+
const workbook = XLSX.read(buffer, { type: 'array' });
|
|
541
|
+
|
|
542
|
+
const sheets: Record<string, DataFrame> = {};
|
|
543
|
+
|
|
544
|
+
workbook.SheetNames.forEach((sheetName: string) => {
|
|
545
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
546
|
+
const jsonData: Record<string, any>[] = XLSX.utils.sheet_to_json(worksheet, { defval: null });
|
|
547
|
+
|
|
548
|
+
if (jsonData.length > 0) {
|
|
549
|
+
sheets[sheetName] = new DataFrame(jsonData);
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
return sheets;
|
|
554
|
+
}
|
|
555
|
+
|
|
525
556
|
/* ═══════════════════════════════════════════════════
|
|
526
557
|
* INTERNAL PARSING HELPERS
|
|
527
558
|
* ═══════════════════════════════════════════════════ */
|