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.
@@ -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;AAMnC,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,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;IAsBpC,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,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;CA+ErE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,CAExF"}
1
+ {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
@@ -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
- const df = await this._driver.streamFile(file);
87
- await this._driver.store(file.name, df, { source: file.name });
88
- this._setDataFrame(df, file.name);
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
- const df = await this._driver.streamFile(file);
124
- await this._driver.store(file.name, df, { source: file.name });
125
- this._setDataFrame(df, file.name);
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;IA8ExE,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAmClB,OAAO,CAAC,SAAS;IAYjB,KAAK,IAAI,IAAI;CAMhB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,CAEhF"}
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
  * ═══════════════════════════════════════════════════ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.176",
3
+ "version": "1.1.177",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",