juxscript 1.1.177 → 1.1.179

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.
@@ -69,7 +69,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
69
69
  paginated(v: boolean): this;
70
70
  rowsPerPage(v: number): this;
71
71
  /**
72
- * ✅ NEW: Render multiple Excel sheets as tabs
72
+ * ✅ FIXED: Render multiple Excel sheets as tabs
73
73
  */
74
74
  private _renderMultiSheet;
75
75
  private _updateStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
@@ -86,7 +86,7 @@ export class DataFrameComponent extends BaseComponent {
86
86
  this.state.loading = true;
87
87
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
88
88
  try {
89
- // ✅ FIX: Check if multi-sheet Excel
89
+ // ✅ Check if multi-sheet Excel
90
90
  const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
91
91
  file.name.toLowerCase().endsWith('.xls');
92
92
  if (isExcel) {
@@ -95,7 +95,7 @@ export class DataFrameComponent extends BaseComponent {
95
95
  // Store first sheet to IndexedDB
96
96
  await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
97
97
  if (sheetNames.length > 1) {
98
- // Multi-sheet: render tabs
98
+ // Multi-sheet: render tabs
99
99
  this._renderMultiSheet(sheets, file.name);
100
100
  }
101
101
  else {
@@ -212,7 +212,7 @@ export class DataFrameComponent extends BaseComponent {
212
212
  * MULTI-SHEET RENDERING
213
213
  * ═══════════════════════════════════════════════════ */
214
214
  /**
215
- * ✅ NEW: Render multiple Excel sheets as tabs
215
+ * ✅ FIXED: Render multiple Excel sheets as tabs
216
216
  */
217
217
  _renderMultiSheet(sheets, sourceName) {
218
218
  this.state.loading = false;
@@ -228,25 +228,27 @@ export class DataFrameComponent extends BaseComponent {
228
228
  Object.entries(sheets).forEach(([name, df]) => {
229
229
  this._sheets.set(name, df);
230
230
  });
231
- // Create tabs
232
231
  const sheetNames = Object.keys(sheets);
232
+ // ✅ FIX: Create tabs with EMPTY content first (just placeholders)
233
233
  const tabs = new Tabs(`${this._id}-tabs`, {
234
234
  tabs: sheetNames.map(name => ({
235
235
  id: name,
236
236
  label: name,
237
- content: '' // We'll populate after render
237
+ content: '' // Empty - we'll fill after render
238
238
  })),
239
239
  activeTab: sheetNames[0]
240
240
  });
241
241
  this._tabs = tabs;
242
- // Render tabs into wrapper
242
+ // Render tabs container
243
243
  const tabsContainer = document.createElement('div');
244
244
  tabsContainer.className = 'jux-dataframe-tabs';
245
245
  wrapper.appendChild(tabsContainer);
246
+ // ✅ Render tabs NOW (creates all tab panels in DOM)
246
247
  tabs.render(tabsContainer);
247
- // Populate each tab with its DataFrame table
248
+ // NOW populate each tab with its DataFrame table (panels exist now)
248
249
  sheetNames.forEach(sheetName => {
249
250
  const df = sheets[sheetName];
251
+ // Create table
250
252
  const table = new Table(`${this._id}-table-${sheetName}`, {
251
253
  striped: this._tableOptions.striped,
252
254
  hoverable: this._tableOptions.hoverable,
@@ -255,14 +257,20 @@ export class DataFrameComponent extends BaseComponent {
255
257
  paginated: this._tableOptions.paginated,
256
258
  rowsPerPage: this._tableOptions.rowsPerPage
257
259
  });
258
- // Convert columns to ColumnDef[]
260
+ // Convert columns to ColumnDef[]
259
261
  const columnDefs = df.columns.map(col => ({
260
262
  key: col,
261
263
  label: col
262
264
  }));
263
265
  table.columns(columnDefs).rows(df.toRows());
264
- // Add table to tab
265
- tabs.addTabContent(sheetName, table);
266
+ // Find the tab panel (it exists now because tabs.render() was called)
267
+ const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
268
+ if (!tabPanel) {
269
+ console.error(`Tab panel not found: ${this._id}-tabs-${sheetName}-panel`);
270
+ return;
271
+ }
272
+ // ✅ Render table directly into tab panel
273
+ table.render(tabPanel);
266
274
  // Add filter if enabled
267
275
  if (this._tableOptions.filterable) {
268
276
  const filterContainer = document.createElement('div');
@@ -290,13 +298,10 @@ export class DataFrameComponent extends BaseComponent {
290
298
  });
291
299
  table.rows(filtered.toRows());
292
300
  });
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
- }
301
+ // Insert filter BEFORE the table wrapper (which exists now)
302
+ const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
303
+ if (tableWrapper) {
304
+ tabPanel.insertBefore(filterContainer, tableWrapper);
300
305
  }
301
306
  }
302
307
  });
@@ -305,7 +310,6 @@ export class DataFrameComponent extends BaseComponent {
305
310
  this._updateStatus(`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`, 'success');
306
311
  // Set first sheet as active DataFrame
307
312
  this._df = sheets[sheetNames[0]];
308
- this._table = tabs._tabsWrapper?.querySelector(`#${this._id}-table-${sheetNames[0]}`);
309
313
  this._triggerCallback('load', this._df, null, this);
310
314
  }
311
315
  /* ═══════════════════════════════════════════════════
@@ -352,19 +356,19 @@ export class DataFrameComponent extends BaseComponent {
352
356
  _updateTable() {
353
357
  if (!this._table || !this._df)
354
358
  return;
355
- // ✅ FIX: Convert string[] columns to ColumnDef[] with labels
359
+ // ✅ Convert string[] columns to ColumnDef[] with labels
356
360
  const columnDefs = this._df.columns.map(col => ({
357
361
  key: col,
358
- label: col // Use column name as label
362
+ label: col
359
363
  }));
360
- // ✅ Update both columns AND rows (force table rebuild)
364
+ // ✅ Update columns and rows
361
365
  this._table.columns(columnDefs).rows(this._df.toRows());
362
- // ✅ Force table to re-render header and body
363
- const tableElement = this._table['_tableElement']; // Access private property
366
+ // ✅ FIX: Force full table rebuild (including pagination)
367
+ const tableElement = this._table['_tableElement'];
364
368
  if (tableElement) {
365
369
  const wrapper = tableElement.closest('.jux-table-wrapper');
366
370
  if (wrapper) {
367
- // Clear and rebuild the entire table
371
+ // Clear table content
368
372
  tableElement.innerHTML = '';
369
373
  // Rebuild header
370
374
  const thead = this._table._buildTableHeader();
@@ -375,6 +379,10 @@ export class DataFrameComponent extends BaseComponent {
375
379
  tableElement.appendChild(tbody);
376
380
  // Re-wire events
377
381
  this._table._wireTriggerEvents(tbody);
382
+ // ✅ FIX: Re-build pagination controls
383
+ if (this._tableOptions.paginated) {
384
+ this._table._updatePagination(wrapper, tbody);
385
+ }
378
386
  }
379
387
  }
380
388
  }
@@ -443,6 +451,7 @@ export class DataFrameComponent extends BaseComponent {
443
451
  }
444
452
  const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
445
453
  this._uploadRef = upload;
454
+ // ✅ FIX: Use the SAME logic as fromUpload() to handle multi-sheet
446
455
  this._pendingSource = async () => {
447
456
  upload.bind('change', async (files) => {
448
457
  if (!files || files.length === 0)
@@ -451,9 +460,29 @@ export class DataFrameComponent extends BaseComponent {
451
460
  this.state.loading = true;
452
461
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
453
462
  try {
454
- const df = await this._driver.streamFile(file);
455
- await this._driver.store(file.name, df, { source: file.name });
456
- this._setDataFrame(df, file.name);
463
+ // Check if multi-sheet Excel
464
+ const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
465
+ file.name.toLowerCase().endsWith('.xls');
466
+ if (isExcel) {
467
+ const sheets = await this._driver.streamFileMultiSheet(file);
468
+ const sheetNames = Object.keys(sheets);
469
+ // Store first sheet to IndexedDB
470
+ await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
471
+ if (sheetNames.length > 1) {
472
+ // ✅ Multi-sheet: render tabs
473
+ this._renderMultiSheet(sheets, file.name);
474
+ }
475
+ else {
476
+ // Single sheet: render normally
477
+ this._setDataFrame(sheets[sheetNames[0]], file.name);
478
+ }
479
+ }
480
+ else {
481
+ // CSV/TSV: single sheet
482
+ const df = await this._driver.streamFile(file);
483
+ await this._driver.store(file.name, df, { source: file.name });
484
+ this._setDataFrame(df, file.name);
485
+ }
457
486
  }
458
487
  catch (err) {
459
488
  this._triggerCallback('error', err.message, null, this);
@@ -124,7 +124,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
124
124
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
125
125
 
126
126
  try {
127
- // ✅ FIX: Check if multi-sheet Excel
127
+ // ✅ Check if multi-sheet Excel
128
128
  const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
129
129
  file.name.toLowerCase().endsWith('.xls');
130
130
 
@@ -136,7 +136,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
136
136
  await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
137
137
 
138
138
  if (sheetNames.length > 1) {
139
- // Multi-sheet: render tabs
139
+ // Multi-sheet: render tabs
140
140
  this._renderMultiSheet(sheets, file.name);
141
141
  } else {
142
142
  // Single sheet: render normally
@@ -262,7 +262,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
262
262
  * ═══════════════════════════════════════════════════ */
263
263
 
264
264
  /**
265
- * ✅ NEW: Render multiple Excel sheets as tabs
265
+ * ✅ FIXED: Render multiple Excel sheets as tabs
266
266
  */
267
267
  private _renderMultiSheet(sheets: Record<string, DataFrame>, sourceName: string): void {
268
268
  this.state.loading = false;
@@ -280,28 +280,33 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
280
280
  this._sheets.set(name, df);
281
281
  });
282
282
 
283
- // Create tabs
284
283
  const sheetNames = Object.keys(sheets);
284
+
285
+ // ✅ FIX: Create tabs with EMPTY content first (just placeholders)
285
286
  const tabs = new Tabs(`${this._id}-tabs`, {
286
287
  tabs: sheetNames.map(name => ({
287
288
  id: name,
288
289
  label: name,
289
- content: '' // We'll populate after render
290
+ content: '' // Empty - we'll fill after render
290
291
  })),
291
292
  activeTab: sheetNames[0]
292
293
  });
293
294
 
294
295
  this._tabs = tabs;
295
296
 
296
- // Render tabs into wrapper
297
+ // Render tabs container
297
298
  const tabsContainer = document.createElement('div');
298
299
  tabsContainer.className = 'jux-dataframe-tabs';
299
300
  wrapper.appendChild(tabsContainer);
301
+
302
+ // ✅ Render tabs NOW (creates all tab panels in DOM)
300
303
  tabs.render(tabsContainer);
301
304
 
302
- // Populate each tab with its DataFrame table
305
+ // NOW populate each tab with its DataFrame table (panels exist now)
303
306
  sheetNames.forEach(sheetName => {
304
307
  const df = sheets[sheetName];
308
+
309
+ // Create table
305
310
  const table = new Table(`${this._id}-table-${sheetName}`, {
306
311
  striped: this._tableOptions.striped,
307
312
  hoverable: this._tableOptions.hoverable,
@@ -311,7 +316,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
311
316
  rowsPerPage: this._tableOptions.rowsPerPage
312
317
  });
313
318
 
314
- // Convert columns to ColumnDef[]
319
+ // Convert columns to ColumnDef[]
315
320
  const columnDefs = df.columns.map(col => ({
316
321
  key: col,
317
322
  label: col
@@ -319,8 +324,15 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
319
324
 
320
325
  table.columns(columnDefs).rows(df.toRows());
321
326
 
322
- // Add table to tab
323
- tabs.addTabContent(sheetName, table);
327
+ // Find the tab panel (it exists now because tabs.render() was called)
328
+ const tabPanel = document.getElementById(`${this._id}-tabs-${sheetName}-panel`);
329
+ if (!tabPanel) {
330
+ console.error(`Tab panel not found: ${this._id}-tabs-${sheetName}-panel`);
331
+ return;
332
+ }
333
+
334
+ // ✅ Render table directly into tab panel
335
+ table.render(tabPanel);
324
336
 
325
337
  // Add filter if enabled
326
338
  if (this._tableOptions.filterable) {
@@ -357,13 +369,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
357
369
  table.rows(filtered.toRows());
358
370
  });
359
371
 
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
- }
372
+ // Insert filter BEFORE the table wrapper (which exists now)
373
+ const tableWrapper = tabPanel.querySelector('.jux-table-wrapper');
374
+ if (tableWrapper) {
375
+ tabPanel.insertBefore(filterContainer, tableWrapper);
367
376
  }
368
377
  }
369
378
  });
@@ -377,7 +386,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
377
386
 
378
387
  // Set first sheet as active DataFrame
379
388
  this._df = sheets[sheetNames[0]];
380
- this._table = (tabs as any)._tabsWrapper?.querySelector(`#${this._id}-table-${sheetNames[0]}`);
381
389
 
382
390
  this._triggerCallback('load', this._df, null, this);
383
391
  }
@@ -438,21 +446,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
438
446
  private _updateTable(): void {
439
447
  if (!this._table || !this._df) return;
440
448
 
441
- // ✅ FIX: Convert string[] columns to ColumnDef[] with labels
449
+ // ✅ Convert string[] columns to ColumnDef[] with labels
442
450
  const columnDefs = this._df.columns.map(col => ({
443
451
  key: col,
444
- label: col // Use column name as label
452
+ label: col
445
453
  }));
446
454
 
447
- // ✅ Update both columns AND rows (force table rebuild)
455
+ // ✅ Update columns and rows
448
456
  this._table.columns(columnDefs).rows(this._df.toRows());
449
457
 
450
- // ✅ Force table to re-render header and body
451
- const tableElement = this._table['_tableElement']; // Access private property
458
+ // ✅ FIX: Force full table rebuild (including pagination)
459
+ const tableElement = this._table['_tableElement'];
452
460
  if (tableElement) {
453
- const wrapper = tableElement.closest('.jux-table-wrapper');
461
+ const wrapper = tableElement.closest('.jux-table-wrapper') as HTMLElement;
454
462
  if (wrapper) {
455
- // Clear and rebuild the entire table
463
+ // Clear table content
456
464
  tableElement.innerHTML = '';
457
465
 
458
466
  // Rebuild header
@@ -466,6 +474,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
466
474
 
467
475
  // Re-wire events
468
476
  (this._table as any)._wireTriggerEvents(tbody);
477
+
478
+ // ✅ FIX: Re-build pagination controls
479
+ if (this._tableOptions.paginated) {
480
+ (this._table as any)._updatePagination(wrapper, tbody);
481
+ }
469
482
  }
470
483
  }
471
484
  }
@@ -545,6 +558,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
545
558
  const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
546
559
 
547
560
  this._uploadRef = upload;
561
+ // ✅ FIX: Use the SAME logic as fromUpload() to handle multi-sheet
548
562
  this._pendingSource = async () => {
549
563
  upload.bind('change', async (files: File[]) => {
550
564
  if (!files || files.length === 0) return;
@@ -553,9 +567,30 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
553
567
  this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
554
568
 
555
569
  try {
556
- const df = await this._driver.streamFile(file);
557
- await this._driver.store(file.name, df, { source: file.name });
558
- this._setDataFrame(df, file.name);
570
+ // Check if multi-sheet Excel
571
+ const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
572
+ file.name.toLowerCase().endsWith('.xls');
573
+
574
+ if (isExcel) {
575
+ const sheets = await this._driver.streamFileMultiSheet(file);
576
+ const sheetNames = Object.keys(sheets);
577
+
578
+ // Store first sheet to IndexedDB
579
+ await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
580
+
581
+ if (sheetNames.length > 1) {
582
+ // ✅ Multi-sheet: render tabs
583
+ this._renderMultiSheet(sheets, file.name);
584
+ } else {
585
+ // Single sheet: render normally
586
+ this._setDataFrame(sheets[sheetNames[0]], file.name);
587
+ }
588
+ } else {
589
+ // CSV/TSV: single sheet
590
+ const df = await this._driver.streamFile(file);
591
+ await this._driver.store(file.name, df, { source: file.name });
592
+ this._setDataFrame(df, file.name);
593
+ }
559
594
  } catch (err: any) {
560
595
  this._triggerCallback('error', err.message, null, this);
561
596
  this.state.loading = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.177",
3
+ "version": "1.1.179",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",