juxscript 1.1.204 → 1.1.205
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.
|
@@ -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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"}
|
|
@@ -217,6 +217,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
217
217
|
}
|
|
218
218
|
});
|
|
219
219
|
const sheetNames = Object.keys(sheets);
|
|
220
|
+
if (sheetNames.length === 0) {
|
|
221
|
+
this._updateStatus('No data found in file', 'error');
|
|
222
|
+
this.state.loading = false;
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
220
225
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
221
226
|
if (sheetNames.length > 1) {
|
|
222
227
|
this._renderMultiSheet(sheets, file.name);
|
|
@@ -251,7 +256,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
251
256
|
const wrapper = document.getElementById(this._id);
|
|
252
257
|
if (!wrapper)
|
|
253
258
|
return;
|
|
254
|
-
// Clean up existing content
|
|
255
259
|
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
256
260
|
if (existingTable)
|
|
257
261
|
existingTable.remove();
|
|
@@ -262,11 +266,10 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
262
266
|
this._sheets.set(name, df);
|
|
263
267
|
});
|
|
264
268
|
const sheetNames = Object.keys(sheets);
|
|
265
|
-
// Build tabs using the Tabs component
|
|
266
269
|
const tabDefs = sheetNames.map(name => ({
|
|
267
270
|
id: name,
|
|
268
271
|
label: name,
|
|
269
|
-
content: ''
|
|
272
|
+
content: ''
|
|
270
273
|
}));
|
|
271
274
|
this._tabs = new Tabs(`${this._id}-tabs`, {
|
|
272
275
|
tabs: tabDefs,
|
|
@@ -275,15 +278,12 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
275
278
|
this._tabs.bind('tabChange', (tabId) => {
|
|
276
279
|
this._df = this._sheets.get(tabId) || null;
|
|
277
280
|
});
|
|
278
|
-
// Create container for tabs
|
|
279
281
|
const tabsContainer = document.createElement('div');
|
|
280
282
|
tabsContainer.className = 'jux-dataframe-tabs';
|
|
281
283
|
wrapper.appendChild(tabsContainer);
|
|
282
284
|
this._tabs.render(tabsContainer);
|
|
283
|
-
|
|
284
|
-
sheetNames.forEach((sheetName, idx) => {
|
|
285
|
+
sheetNames.forEach((sheetName) => {
|
|
285
286
|
const df = sheets[sheetName];
|
|
286
|
-
const panelId = `${this._id}-tabs-${sheetName}-panel`;
|
|
287
287
|
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
288
288
|
striped: this._tableOptions.striped,
|
|
289
289
|
hoverable: this._tableOptions.hoverable,
|
|
@@ -294,48 +294,13 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
294
294
|
});
|
|
295
295
|
const columnDefs = df.columns.map(col => ({ key: col, label: col }));
|
|
296
296
|
table.columns(columnDefs).rows(df.toRows());
|
|
297
|
-
// Add settings button to tab panel
|
|
298
297
|
const settingsBtn = new Button(`${this._id}-settings-${sheetName}`, {
|
|
299
298
|
label: '⚙️ Import Settings',
|
|
300
299
|
variant: 'ghost',
|
|
301
300
|
size: 'small'
|
|
302
301
|
});
|
|
303
302
|
settingsBtn.bind('click', () => this._showReshapeModal());
|
|
304
|
-
// Use addTabContent to add components
|
|
305
303
|
this._tabs.addTabContent(sheetName, [settingsBtn, table]);
|
|
306
|
-
if (this._tableOptions.filterable) {
|
|
307
|
-
// Add filter input above table
|
|
308
|
-
const panel = document.getElementById(panelId);
|
|
309
|
-
if (panel) {
|
|
310
|
-
const filterContainer = document.createElement('div');
|
|
311
|
-
filterContainer.className = 'jux-dataframe-filter';
|
|
312
|
-
const input = document.createElement('input');
|
|
313
|
-
input.type = 'text';
|
|
314
|
-
input.placeholder = `Filter ${sheetName}...`;
|
|
315
|
-
input.className = 'jux-input-element jux-dataframe-filter-input';
|
|
316
|
-
const iconEl = renderIcon('search');
|
|
317
|
-
iconEl.style.width = '16px';
|
|
318
|
-
iconEl.style.height = '16px';
|
|
319
|
-
const iconWrap = document.createElement('span');
|
|
320
|
-
iconWrap.className = 'jux-dataframe-filter-icon';
|
|
321
|
-
iconWrap.appendChild(iconEl);
|
|
322
|
-
filterContainer.appendChild(iconWrap);
|
|
323
|
-
filterContainer.appendChild(input);
|
|
324
|
-
input.addEventListener('input', () => {
|
|
325
|
-
const text = input.value.toLowerCase();
|
|
326
|
-
if (!text) {
|
|
327
|
-
table.rows(df.toRows());
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const filtered = df.filter((row) => Object.values(row).some(v => v !== null && v !== undefined && String(v).toLowerCase().includes(text)));
|
|
331
|
-
table.rows(filtered.toRows());
|
|
332
|
-
});
|
|
333
|
-
const tableWrapper = panel.querySelector('.jux-table-wrapper');
|
|
334
|
-
if (tableWrapper) {
|
|
335
|
-
panel.insertBefore(filterContainer, tableWrapper);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
304
|
});
|
|
340
305
|
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
341
306
|
this._updateStatus(`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`, 'success');
|
|
@@ -378,7 +343,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
378
343
|
const emptyColumns = cols.filter(c => {
|
|
379
344
|
if (!c.startsWith('__EMPTY'))
|
|
380
345
|
return false;
|
|
381
|
-
// Check if ALL values in this column are null/empty
|
|
382
346
|
return rows.every(row => {
|
|
383
347
|
const val = row[c];
|
|
384
348
|
return val === null || val === undefined || String(val).trim() === '';
|
|
@@ -411,7 +375,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
411
375
|
}
|
|
412
376
|
else {
|
|
413
377
|
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
|
|
414
|
-
// Always show settings button if we have raw file data
|
|
415
378
|
if (this._rawFileData) {
|
|
416
379
|
requestAnimationFrame(() => {
|
|
417
380
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
@@ -455,6 +418,10 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
455
418
|
}
|
|
456
419
|
_detectLikelyHeaderRow(df) {
|
|
457
420
|
const rows = df.toRows();
|
|
421
|
+
const cols = df.columns;
|
|
422
|
+
const colsAreGeneric = cols.some(c => c.startsWith('__EMPTY') || c.match(/^_\d+$/) || c.match(/^col_\d+$/));
|
|
423
|
+
if (!colsAreGeneric)
|
|
424
|
+
return 0;
|
|
458
425
|
for (let i = 0; i < Math.min(rows.length, 10); i++) {
|
|
459
426
|
const row = rows[i];
|
|
460
427
|
const values = Object.values(row);
|
|
@@ -465,10 +432,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
465
432
|
const str = String(v).trim();
|
|
466
433
|
return isNaN(Number(str)) && str !== '';
|
|
467
434
|
}).length;
|
|
468
|
-
if (nonNumericCount >= nonEmpty.length * 0.7
|
|
469
|
-
// i
|
|
470
|
-
// as the header during the initial parse, so the actual 0-based file
|
|
471
|
-
// row index is i + 1.
|
|
435
|
+
if (nonNumericCount >= nonEmpty.length * 0.7) {
|
|
436
|
+
// toRows index i = file row (i + 1) since row 0 was used as headers
|
|
472
437
|
return i + 1;
|
|
473
438
|
}
|
|
474
439
|
}
|
|
@@ -500,7 +465,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
500
465
|
if (!this._rawFileData?.file)
|
|
501
466
|
return;
|
|
502
467
|
this._cleanupReshapeModal();
|
|
503
|
-
// Always detect from a fresh raw parse, not from current _df
|
|
504
468
|
let suggestedRow = 0;
|
|
505
469
|
try {
|
|
506
470
|
const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
@@ -526,22 +490,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
526
490
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
527
491
|
Header Row (0-based index)
|
|
528
492
|
</label>
|
|
529
|
-
<input
|
|
530
|
-
|
|
531
|
-
id="${this._id}-header-row"
|
|
532
|
-
class="jux-input-element"
|
|
533
|
-
value="${suggestedRow}"
|
|
534
|
-
min="0"
|
|
535
|
-
max="50"
|
|
536
|
-
style="width: 100%;"
|
|
537
|
-
/>
|
|
538
|
-
<div id="${this._id}-reshape-hint" class="jux-reshape-hint" style="margin-top: 0.5rem; padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;">
|
|
539
|
-
</div>
|
|
493
|
+
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="${suggestedRow}" min="0" max="50" style="width: 100%;" />
|
|
494
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint" style="margin-top: 0.5rem; padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;"></div>
|
|
540
495
|
</div>
|
|
541
496
|
<div class="jux-reshape-preview-container">
|
|
542
|
-
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
543
|
-
Preview
|
|
544
|
-
</div>
|
|
497
|
+
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
|
|
545
498
|
<div id="${this._id}-preview" style="font-family: ui-monospace, monospace; font-size: 12px; background: hsl(var(--muted) / 0.3); border: 1px solid hsl(var(--border)); border-radius: var(--radius); padding: 0; overflow: hidden; max-height: 400px; overflow-y: auto;"></div>
|
|
546
499
|
</div>
|
|
547
500
|
`;
|
|
@@ -599,8 +552,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
599
552
|
if (!hintDiv)
|
|
600
553
|
return;
|
|
601
554
|
if (headerRow > 0) {
|
|
602
|
-
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers.
|
|
603
|
-
`Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
555
|
+
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers. Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
604
556
|
}
|
|
605
557
|
else {
|
|
606
558
|
hintDiv.innerHTML = `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
@@ -610,26 +562,46 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
610
562
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
611
563
|
updateHint(headerRow);
|
|
612
564
|
try {
|
|
613
|
-
//
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
|
|
565
|
+
// Read cells directly from XLSX to match parser behavior exactly
|
|
566
|
+
const XLSX = await import('xlsx');
|
|
567
|
+
const buffer = await this._rawFileData.file.arrayBuffer();
|
|
568
|
+
const workbook = XLSX.read(buffer, {
|
|
569
|
+
type: 'array',
|
|
570
|
+
sheetRows: Math.max(headerRow + 12, 15),
|
|
571
|
+
dense: false
|
|
617
572
|
});
|
|
618
|
-
const
|
|
619
|
-
|
|
573
|
+
const sheetName = workbook.SheetNames[0];
|
|
574
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
575
|
+
const ref = worksheet['!ref'];
|
|
576
|
+
if (!ref) {
|
|
620
577
|
if (previewDiv)
|
|
621
578
|
previewDiv.textContent = 'No data found';
|
|
622
579
|
return;
|
|
623
580
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const
|
|
627
|
-
const
|
|
628
|
-
|
|
581
|
+
const range = XLSX.utils.decode_range(ref);
|
|
582
|
+
const endRow = range.e.r;
|
|
583
|
+
const startCol = range.s.c;
|
|
584
|
+
const endCol = range.e.c;
|
|
585
|
+
const readCellValue = (r, c) => {
|
|
586
|
+
const addr = XLSX.utils.encode_cell({ r, c });
|
|
587
|
+
const cell = worksheet[addr];
|
|
588
|
+
if (!cell)
|
|
589
|
+
return null;
|
|
590
|
+
if (cell.w !== undefined)
|
|
591
|
+
return cell.w;
|
|
592
|
+
if (cell.v !== undefined)
|
|
593
|
+
return cell.v;
|
|
594
|
+
return null;
|
|
595
|
+
};
|
|
596
|
+
const readRow = (r) => {
|
|
597
|
+
const vals = [];
|
|
598
|
+
for (let c = startCol; c <= endCol; c++) {
|
|
599
|
+
vals.push(readCellValue(r, c));
|
|
600
|
+
}
|
|
601
|
+
return vals;
|
|
602
|
+
};
|
|
629
603
|
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 12px;">';
|
|
630
|
-
|
|
631
|
-
// Row 0 = rawCols, Row 1+ = rawRows[i-1]
|
|
632
|
-
const totalRowsToShow = Math.min(headerRow + 8, rawRows.length + 1);
|
|
604
|
+
const totalRowsToShow = Math.min(headerRow + 8, endRow + 1);
|
|
633
605
|
for (let fileRow = 0; fileRow < totalRowsToShow; fileRow++) {
|
|
634
606
|
const isHeader = (fileRow === headerRow);
|
|
635
607
|
const isSkipped = (fileRow < headerRow);
|
|
@@ -641,7 +613,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
641
613
|
rowStyle += 'background: hsl(var(--muted) / 0.4); color: hsl(var(--muted-foreground)); font-style: italic; opacity: 0.7;';
|
|
642
614
|
}
|
|
643
615
|
html += `<tr style="${rowStyle}">`;
|
|
644
|
-
// Row index cell
|
|
645
616
|
html += `<td style="padding: 8px 12px; width: 60px; font-weight: 600; color: hsl(var(--muted-foreground)); border-right: 1px solid hsl(var(--border)); text-align: center;">`;
|
|
646
617
|
if (isHeader) {
|
|
647
618
|
html += `<span style="color: hsl(142 71% 45%);">▶ ${fileRow}</span>`;
|
|
@@ -650,15 +621,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
650
621
|
html += `${fileRow}`;
|
|
651
622
|
}
|
|
652
623
|
html += '</td>';
|
|
653
|
-
|
|
654
|
-
let values;
|
|
655
|
-
if (fileRow === 0) {
|
|
656
|
-
values = rawCols;
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
values = rawRows[fileRow - 1] ? Object.values(rawRows[fileRow - 1]) : [];
|
|
660
|
-
}
|
|
661
|
-
// Show first 6 columns
|
|
624
|
+
const values = readRow(fileRow);
|
|
662
625
|
const displayCols = values.slice(0, 6);
|
|
663
626
|
displayCols.forEach(val => {
|
|
664
627
|
const displayVal = val != null ? String(val).substring(0, 20) : '';
|
|
@@ -668,26 +631,23 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
668
631
|
html += `<td style="${cellStyle}">${this._escapeHtml(displayVal)}</td>`;
|
|
669
632
|
});
|
|
670
633
|
if (values.length > 6) {
|
|
671
|
-
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));"
|
|
634
|
+
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
672
635
|
}
|
|
673
|
-
// Status badge cell
|
|
674
636
|
html += `<td style="padding: 8px 12px; text-align: right; white-space: nowrap;">`;
|
|
675
637
|
if (isHeader) {
|
|
676
|
-
html += '<span style="background: hsl(
|
|
638
|
+
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
677
639
|
}
|
|
678
640
|
else if (isSkipped) {
|
|
679
|
-
html += '<span style="color: hsl(var(--muted-foreground));
|
|
641
|
+
html += '<span style="color: hsl(var(--muted-foreground));">skipped</span>';
|
|
680
642
|
}
|
|
681
643
|
else {
|
|
682
|
-
html += '<span style="color: hsl(var(--
|
|
644
|
+
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
683
645
|
}
|
|
684
|
-
html += '</td>';
|
|
685
|
-
html += '</tr>';
|
|
646
|
+
html += '</td></tr>';
|
|
686
647
|
}
|
|
687
648
|
html += '</table>';
|
|
688
|
-
if (previewDiv)
|
|
649
|
+
if (previewDiv)
|
|
689
650
|
previewDiv.innerHTML = html;
|
|
690
|
-
}
|
|
691
651
|
}
|
|
692
652
|
catch (err) {
|
|
693
653
|
if (previewDiv)
|
|
@@ -791,8 +751,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
791
751
|
return;
|
|
792
752
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
793
753
|
if (headerRow > 0) {
|
|
794
|
-
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers.
|
|
795
|
-
`Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
754
|
+
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers. Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
796
755
|
}
|
|
797
756
|
else {
|
|
798
757
|
hintDiv.innerHTML = `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
@@ -805,7 +764,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
805
764
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
806
765
|
updateHint();
|
|
807
766
|
try {
|
|
808
|
-
// Parse raw to show all rows
|
|
809
767
|
const rawDf = this._driver.parseCSV(this._rawFileData.text, {
|
|
810
768
|
delimiter: delim,
|
|
811
769
|
headerRow: 0,
|
|
@@ -814,7 +772,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
814
772
|
});
|
|
815
773
|
const rawCols = rawDf.columns;
|
|
816
774
|
const rawRows = rawDf.toRows();
|
|
817
|
-
// Build HTML table
|
|
818
775
|
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 11px;">';
|
|
819
776
|
const totalRows = Math.min(headerRow + 8, rawRows.length + 1);
|
|
820
777
|
for (let i = 0; i < totalRows; i++) {
|
|
@@ -828,11 +785,9 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
828
785
|
rowStyle += 'background: hsl(var(--muted) / 0.3); color: hsl(var(--muted-foreground)); font-style: italic;';
|
|
829
786
|
}
|
|
830
787
|
html += `<tr style="${rowStyle}">`;
|
|
831
|
-
// Row index
|
|
832
788
|
html += `<td style="padding: 6px 8px; width: 50px; color: hsl(var(--muted-foreground)); font-weight: 500;">`;
|
|
833
789
|
html += isHeader ? `<strong>→ ${i}</strong>` : `${i}`;
|
|
834
790
|
html += '</td>';
|
|
835
|
-
// Data
|
|
836
791
|
let values;
|
|
837
792
|
if (i === 0) {
|
|
838
793
|
values = rawCols;
|
|
@@ -850,7 +805,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
850
805
|
if (values.length > 6) {
|
|
851
806
|
html += `<td style="padding: 6px 8px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
852
807
|
}
|
|
853
|
-
// Status
|
|
854
808
|
html += `<td style="padding: 6px 8px; text-align: right; font-size: 10px;">`;
|
|
855
809
|
if (isHeader) {
|
|
856
810
|
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
@@ -861,8 +815,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
861
815
|
else {
|
|
862
816
|
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
863
817
|
}
|
|
864
|
-
html += '</td>';
|
|
865
|
-
html += '</tr>';
|
|
818
|
+
html += '</td></tr>';
|
|
866
819
|
}
|
|
867
820
|
html += '</table>';
|
|
868
821
|
if (previewDiv)
|
|
@@ -275,6 +275,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
275
275
|
});
|
|
276
276
|
|
|
277
277
|
const sheetNames = Object.keys(sheets);
|
|
278
|
+
|
|
279
|
+
if (sheetNames.length === 0) {
|
|
280
|
+
this._updateStatus('No data found in file', 'error');
|
|
281
|
+
this.state.loading = false;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
278
285
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
279
286
|
|
|
280
287
|
if (sheetNames.length > 1) {
|
|
@@ -312,7 +319,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
312
319
|
const wrapper = document.getElementById(this._id);
|
|
313
320
|
if (!wrapper) return;
|
|
314
321
|
|
|
315
|
-
// Clean up existing content
|
|
316
322
|
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
317
323
|
if (existingTable) existingTable.remove();
|
|
318
324
|
|
|
@@ -325,11 +331,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
325
331
|
|
|
326
332
|
const sheetNames = Object.keys(sheets);
|
|
327
333
|
|
|
328
|
-
// Build tabs using the Tabs component
|
|
329
334
|
const tabDefs = sheetNames.map(name => ({
|
|
330
335
|
id: name,
|
|
331
336
|
label: name,
|
|
332
|
-
content: ''
|
|
337
|
+
content: ''
|
|
333
338
|
}));
|
|
334
339
|
|
|
335
340
|
this._tabs = new Tabs(`${this._id}-tabs`, {
|
|
@@ -341,17 +346,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
341
346
|
this._df = this._sheets.get(tabId) || null;
|
|
342
347
|
});
|
|
343
348
|
|
|
344
|
-
// Create container for tabs
|
|
345
349
|
const tabsContainer = document.createElement('div');
|
|
346
350
|
tabsContainer.className = 'jux-dataframe-tabs';
|
|
347
351
|
wrapper.appendChild(tabsContainer);
|
|
348
352
|
|
|
349
353
|
this._tabs.render(tabsContainer);
|
|
350
354
|
|
|
351
|
-
|
|
352
|
-
sheetNames.forEach((sheetName, idx) => {
|
|
355
|
+
sheetNames.forEach((sheetName) => {
|
|
353
356
|
const df = sheets[sheetName];
|
|
354
|
-
const panelId = `${this._id}-tabs-${sheetName}-panel`;
|
|
355
357
|
|
|
356
358
|
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
357
359
|
striped: this._tableOptions.striped,
|
|
@@ -365,7 +367,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
365
367
|
const columnDefs = df.columns.map(col => ({ key: col, label: col }));
|
|
366
368
|
table.columns(columnDefs).rows(df.toRows());
|
|
367
369
|
|
|
368
|
-
// Add settings button to tab panel
|
|
369
370
|
const settingsBtn = new Button(`${this._id}-settings-${sheetName}`, {
|
|
370
371
|
label: '⚙️ Import Settings',
|
|
371
372
|
variant: 'ghost',
|
|
@@ -373,49 +374,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
373
374
|
});
|
|
374
375
|
settingsBtn.bind('click', () => this._showReshapeModal());
|
|
375
376
|
|
|
376
|
-
// Use addTabContent to add components
|
|
377
377
|
this._tabs!.addTabContent(sheetName, [settingsBtn, table]);
|
|
378
|
-
|
|
379
|
-
if (this._tableOptions.filterable) {
|
|
380
|
-
// Add filter input above table
|
|
381
|
-
const panel = document.getElementById(panelId);
|
|
382
|
-
if (panel) {
|
|
383
|
-
const filterContainer = document.createElement('div');
|
|
384
|
-
filterContainer.className = 'jux-dataframe-filter';
|
|
385
|
-
|
|
386
|
-
const input = document.createElement('input');
|
|
387
|
-
input.type = 'text';
|
|
388
|
-
input.placeholder = `Filter ${sheetName}...`;
|
|
389
|
-
input.className = 'jux-input-element jux-dataframe-filter-input';
|
|
390
|
-
|
|
391
|
-
const iconEl = renderIcon('search');
|
|
392
|
-
iconEl.style.width = '16px';
|
|
393
|
-
iconEl.style.height = '16px';
|
|
394
|
-
|
|
395
|
-
const iconWrap = document.createElement('span');
|
|
396
|
-
iconWrap.className = 'jux-dataframe-filter-icon';
|
|
397
|
-
iconWrap.appendChild(iconEl);
|
|
398
|
-
|
|
399
|
-
filterContainer.appendChild(iconWrap);
|
|
400
|
-
filterContainer.appendChild(input);
|
|
401
|
-
|
|
402
|
-
input.addEventListener('input', () => {
|
|
403
|
-
const text = input.value.toLowerCase();
|
|
404
|
-
if (!text) { table.rows(df.toRows()); return; }
|
|
405
|
-
const filtered = df.filter((row) =>
|
|
406
|
-
Object.values(row).some(v =>
|
|
407
|
-
v !== null && v !== undefined && String(v).toLowerCase().includes(text)
|
|
408
|
-
)
|
|
409
|
-
);
|
|
410
|
-
table.rows(filtered.toRows());
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
const tableWrapper = panel.querySelector('.jux-table-wrapper');
|
|
414
|
-
if (tableWrapper) {
|
|
415
|
-
panel.insertBefore(filterContainer, tableWrapper);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
378
|
});
|
|
420
379
|
|
|
421
380
|
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
@@ -468,7 +427,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
468
427
|
const rows = df.toRows();
|
|
469
428
|
const emptyColumns = cols.filter(c => {
|
|
470
429
|
if (!c.startsWith('__EMPTY')) return false;
|
|
471
|
-
// Check if ALL values in this column are null/empty
|
|
472
430
|
return rows.every(row => {
|
|
473
431
|
const val = row[c];
|
|
474
432
|
return val === null || val === undefined || String(val).trim() === '';
|
|
@@ -494,7 +452,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
494
452
|
`${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may need reformatting)`,
|
|
495
453
|
'warning'
|
|
496
454
|
);
|
|
497
|
-
|
|
498
455
|
requestAnimationFrame(() => {
|
|
499
456
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
500
457
|
if (statusEl) {
|
|
@@ -511,8 +468,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
511
468
|
`${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols`,
|
|
512
469
|
'success'
|
|
513
470
|
);
|
|
514
|
-
|
|
515
|
-
// Always show settings button if we have raw file data
|
|
516
471
|
if (this._rawFileData) {
|
|
517
472
|
requestAnimationFrame(() => {
|
|
518
473
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
@@ -566,6 +521,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
566
521
|
|
|
567
522
|
private _detectLikelyHeaderRow(df: DataFrame): number {
|
|
568
523
|
const rows = df.toRows();
|
|
524
|
+
const cols = df.columns;
|
|
525
|
+
|
|
526
|
+
const colsAreGeneric = cols.some(c =>
|
|
527
|
+
c.startsWith('__EMPTY') || c.match(/^_\d+$/) || c.match(/^col_\d+$/)
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
if (!colsAreGeneric) return 0;
|
|
569
531
|
|
|
570
532
|
for (let i = 0; i < Math.min(rows.length, 10); i++) {
|
|
571
533
|
const row = rows[i];
|
|
@@ -579,10 +541,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
579
541
|
return isNaN(Number(str)) && str !== '';
|
|
580
542
|
}).length;
|
|
581
543
|
|
|
582
|
-
if (nonNumericCount >= nonEmpty.length * 0.7
|
|
583
|
-
// i
|
|
584
|
-
// as the header during the initial parse, so the actual 0-based file
|
|
585
|
-
// row index is i + 1.
|
|
544
|
+
if (nonNumericCount >= nonEmpty.length * 0.7) {
|
|
545
|
+
// toRows index i = file row (i + 1) since row 0 was used as headers
|
|
586
546
|
return i + 1;
|
|
587
547
|
}
|
|
588
548
|
}
|
|
@@ -618,7 +578,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
618
578
|
|
|
619
579
|
this._cleanupReshapeModal();
|
|
620
580
|
|
|
621
|
-
// Always detect from a fresh raw parse, not from current _df
|
|
622
581
|
let suggestedRow = 0;
|
|
623
582
|
try {
|
|
624
583
|
const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
@@ -645,22 +604,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
645
604
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
646
605
|
Header Row (0-based index)
|
|
647
606
|
</label>
|
|
648
|
-
<input
|
|
649
|
-
|
|
650
|
-
id="${this._id}-header-row"
|
|
651
|
-
class="jux-input-element"
|
|
652
|
-
value="${suggestedRow}"
|
|
653
|
-
min="0"
|
|
654
|
-
max="50"
|
|
655
|
-
style="width: 100%;"
|
|
656
|
-
/>
|
|
657
|
-
<div id="${this._id}-reshape-hint" class="jux-reshape-hint" style="margin-top: 0.5rem; padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;">
|
|
658
|
-
</div>
|
|
607
|
+
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="${suggestedRow}" min="0" max="50" style="width: 100%;" />
|
|
608
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint" style="margin-top: 0.5rem; padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;"></div>
|
|
659
609
|
</div>
|
|
660
610
|
<div class="jux-reshape-preview-container">
|
|
661
|
-
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
662
|
-
Preview
|
|
663
|
-
</div>
|
|
611
|
+
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
|
|
664
612
|
<div id="${this._id}-preview" style="font-family: ui-monospace, monospace; font-size: 12px; background: hsl(var(--muted) / 0.3); border: 1px solid hsl(var(--border)); border-radius: var(--radius); padding: 0; overflow: hidden; max-height: 400px; overflow-y: auto;"></div>
|
|
665
613
|
</div>
|
|
666
614
|
`;
|
|
@@ -727,8 +675,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
727
675
|
const updateHint = (headerRow: number) => {
|
|
728
676
|
if (!hintDiv) return;
|
|
729
677
|
if (headerRow > 0) {
|
|
730
|
-
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers.
|
|
731
|
-
`Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
678
|
+
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers. Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
732
679
|
} else {
|
|
733
680
|
hintDiv.innerHTML = `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
734
681
|
}
|
|
@@ -739,36 +686,53 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
739
686
|
updateHint(headerRow);
|
|
740
687
|
|
|
741
688
|
try {
|
|
742
|
-
//
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
689
|
+
// Read cells directly from XLSX to match parser behavior exactly
|
|
690
|
+
const XLSX = await import('xlsx');
|
|
691
|
+
const buffer = await this._rawFileData!.file.arrayBuffer();
|
|
692
|
+
const workbook = XLSX.read(buffer, {
|
|
693
|
+
type: 'array',
|
|
694
|
+
sheetRows: Math.max(headerRow + 12, 15),
|
|
695
|
+
dense: false
|
|
746
696
|
});
|
|
747
|
-
const rawSheet = Object.values(rawSheets)[0];
|
|
748
697
|
|
|
749
|
-
|
|
698
|
+
const sheetName = workbook.SheetNames[0];
|
|
699
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
700
|
+
const ref = worksheet['!ref'];
|
|
701
|
+
if (!ref) {
|
|
750
702
|
if (previewDiv) previewDiv.textContent = 'No data found';
|
|
751
703
|
return;
|
|
752
704
|
}
|
|
753
705
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const
|
|
757
|
-
const
|
|
706
|
+
const range = XLSX.utils.decode_range(ref);
|
|
707
|
+
const endRow = range.e.r;
|
|
708
|
+
const startCol = range.s.c;
|
|
709
|
+
const endCol = range.e.c;
|
|
710
|
+
|
|
711
|
+
const readCellValue = (r: number, c: number): any => {
|
|
712
|
+
const addr = XLSX.utils.encode_cell({ r, c });
|
|
713
|
+
const cell = worksheet[addr];
|
|
714
|
+
if (!cell) return null;
|
|
715
|
+
if (cell.w !== undefined) return cell.w;
|
|
716
|
+
if (cell.v !== undefined) return cell.v;
|
|
717
|
+
return null;
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const readRow = (r: number): any[] => {
|
|
721
|
+
const vals: any[] = [];
|
|
722
|
+
for (let c = startCol; c <= endCol; c++) {
|
|
723
|
+
vals.push(readCellValue(r, c));
|
|
724
|
+
}
|
|
725
|
+
return vals;
|
|
726
|
+
};
|
|
758
727
|
|
|
759
|
-
// Build HTML table showing raw file structure
|
|
760
728
|
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 12px;">';
|
|
761
|
-
|
|
762
|
-
// We need to show rows 0 through headerRow+7 (or so)
|
|
763
|
-
// Row 0 = rawCols, Row 1+ = rawRows[i-1]
|
|
764
|
-
const totalRowsToShow = Math.min(headerRow + 8, rawRows.length + 1);
|
|
729
|
+
const totalRowsToShow = Math.min(headerRow + 8, endRow + 1);
|
|
765
730
|
|
|
766
731
|
for (let fileRow = 0; fileRow < totalRowsToShow; fileRow++) {
|
|
767
732
|
const isHeader = (fileRow === headerRow);
|
|
768
733
|
const isSkipped = (fileRow < headerRow);
|
|
769
734
|
|
|
770
735
|
let rowStyle = 'border-bottom: 1px solid hsl(var(--border));';
|
|
771
|
-
|
|
772
736
|
if (isHeader) {
|
|
773
737
|
rowStyle += 'background: hsl(142 71% 45% / 0.15); font-weight: 600;';
|
|
774
738
|
} else if (isSkipped) {
|
|
@@ -776,8 +740,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
776
740
|
}
|
|
777
741
|
|
|
778
742
|
html += `<tr style="${rowStyle}">`;
|
|
779
|
-
|
|
780
|
-
// Row index cell
|
|
781
743
|
html += `<td style="padding: 8px 12px; width: 60px; font-weight: 600; color: hsl(var(--muted-foreground)); border-right: 1px solid hsl(var(--border)); text-align: center;">`;
|
|
782
744
|
if (isHeader) {
|
|
783
745
|
html += `<span style="color: hsl(142 71% 45%);">▶ ${fileRow}</span>`;
|
|
@@ -786,15 +748,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
786
748
|
}
|
|
787
749
|
html += '</td>';
|
|
788
750
|
|
|
789
|
-
|
|
790
|
-
let values: any[];
|
|
791
|
-
if (fileRow === 0) {
|
|
792
|
-
values = rawCols;
|
|
793
|
-
} else {
|
|
794
|
-
values = rawRows[fileRow - 1] ? Object.values(rawRows[fileRow - 1]) : [];
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// Show first 6 columns
|
|
751
|
+
const values = readRow(fileRow);
|
|
798
752
|
const displayCols = values.slice(0, 6);
|
|
799
753
|
displayCols.forEach(val => {
|
|
800
754
|
const displayVal = val != null ? String(val).substring(0, 20) : '';
|
|
@@ -805,28 +759,22 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
805
759
|
});
|
|
806
760
|
|
|
807
761
|
if (values.length > 6) {
|
|
808
|
-
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));"
|
|
762
|
+
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
809
763
|
}
|
|
810
764
|
|
|
811
|
-
// Status badge cell
|
|
812
765
|
html += `<td style="padding: 8px 12px; text-align: right; white-space: nowrap;">`;
|
|
813
766
|
if (isHeader) {
|
|
814
|
-
html += '<span style="background: hsl(
|
|
767
|
+
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
815
768
|
} else if (isSkipped) {
|
|
816
|
-
html += '<span style="color: hsl(var(--muted-foreground));
|
|
769
|
+
html += '<span style="color: hsl(var(--muted-foreground));">skipped</span>';
|
|
817
770
|
} else {
|
|
818
|
-
html += '<span style="color: hsl(var(--
|
|
771
|
+
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
819
772
|
}
|
|
820
|
-
html += '</td>';
|
|
821
|
-
|
|
822
|
-
html += '</tr>';
|
|
773
|
+
html += '</td></tr>';
|
|
823
774
|
}
|
|
824
775
|
|
|
825
776
|
html += '</table>';
|
|
826
|
-
|
|
827
|
-
if (previewDiv) {
|
|
828
|
-
previewDiv.innerHTML = html;
|
|
829
|
-
}
|
|
777
|
+
if (previewDiv) previewDiv.innerHTML = html;
|
|
830
778
|
} catch (err: any) {
|
|
831
779
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|
|
832
780
|
}
|
|
@@ -834,7 +782,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
834
782
|
|
|
835
783
|
if (headerRowInput) headerRowInput.addEventListener('input', updatePreview);
|
|
836
784
|
updatePreview();
|
|
837
|
-
|
|
838
785
|
this._reshapeModal.open();
|
|
839
786
|
}
|
|
840
787
|
|
|
@@ -909,7 +856,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
909
856
|
|
|
910
857
|
await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
|
|
911
858
|
this._setDataFrame(df, this._rawFileData.file.name);
|
|
912
|
-
|
|
913
859
|
this._reshapeModal!.closeModal();
|
|
914
860
|
} catch (err: any) {
|
|
915
861
|
this._updateStatus(`Error: ${err.message}`, 'error');
|
|
@@ -940,8 +886,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
940
886
|
if (!hintDiv) return;
|
|
941
887
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
942
888
|
if (headerRow > 0) {
|
|
943
|
-
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers.
|
|
944
|
-
`Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
889
|
+
hintDiv.innerHTML = `Row <strong>${headerRow}</strong> will be used as column headers. Rows <strong>0–${headerRow - 1}</strong> will be skipped.`;
|
|
945
890
|
} else {
|
|
946
891
|
hintDiv.innerHTML = `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
947
892
|
}
|
|
@@ -952,11 +897,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
952
897
|
|
|
953
898
|
const delim = delimiterSelect?.value || ',';
|
|
954
899
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
955
|
-
|
|
956
900
|
updateHint();
|
|
957
901
|
|
|
958
902
|
try {
|
|
959
|
-
// Parse raw to show all rows
|
|
960
903
|
const rawDf = this._driver.parseCSV(this._rawFileData.text, {
|
|
961
904
|
delimiter: delim,
|
|
962
905
|
headerRow: 0,
|
|
@@ -967,9 +910,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
967
910
|
const rawCols = rawDf.columns;
|
|
968
911
|
const rawRows = rawDf.toRows();
|
|
969
912
|
|
|
970
|
-
// Build HTML table
|
|
971
913
|
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 11px;">';
|
|
972
|
-
|
|
973
914
|
const totalRows = Math.min(headerRow + 8, rawRows.length + 1);
|
|
974
915
|
|
|
975
916
|
for (let i = 0; i < totalRows; i++) {
|
|
@@ -977,7 +918,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
977
918
|
const isSkipped = (i < headerRow);
|
|
978
919
|
|
|
979
920
|
let rowStyle = 'border-bottom: 1px solid hsl(var(--border));';
|
|
980
|
-
|
|
981
921
|
if (isHeader) {
|
|
982
922
|
rowStyle += 'background: hsl(var(--primary) / 0.15); font-weight: bold;';
|
|
983
923
|
} else if (isSkipped) {
|
|
@@ -985,13 +925,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
985
925
|
}
|
|
986
926
|
|
|
987
927
|
html += `<tr style="${rowStyle}">`;
|
|
988
|
-
|
|
989
|
-
// Row index
|
|
990
928
|
html += `<td style="padding: 6px 8px; width: 50px; color: hsl(var(--muted-foreground)); font-weight: 500;">`;
|
|
991
929
|
html += isHeader ? `<strong>→ ${i}</strong>` : `${i}`;
|
|
992
930
|
html += '</td>';
|
|
993
931
|
|
|
994
|
-
// Data
|
|
995
932
|
let values: any[];
|
|
996
933
|
if (i === 0) {
|
|
997
934
|
values = rawCols;
|
|
@@ -1010,7 +947,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1010
947
|
html += `<td style="padding: 6px 8px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
1011
948
|
}
|
|
1012
949
|
|
|
1013
|
-
// Status
|
|
1014
950
|
html += `<td style="padding: 6px 8px; text-align: right; font-size: 10px;">`;
|
|
1015
951
|
if (isHeader) {
|
|
1016
952
|
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
@@ -1019,13 +955,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1019
955
|
} else {
|
|
1020
956
|
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
1021
957
|
}
|
|
1022
|
-
html += '</td>';
|
|
1023
|
-
|
|
1024
|
-
html += '</tr>';
|
|
958
|
+
html += '</td></tr>';
|
|
1025
959
|
}
|
|
1026
960
|
|
|
1027
961
|
html += '</table>';
|
|
1028
|
-
|
|
1029
962
|
if (previewDiv) previewDiv.innerHTML = html;
|
|
1030
963
|
} catch (err: any) {
|
|
1031
964
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -537,7 +537,6 @@ export class TabularDriver {
|
|
|
537
537
|
const cell = worksheet[addr];
|
|
538
538
|
if (!cell)
|
|
539
539
|
return null;
|
|
540
|
-
// Prefer formatted string (w) for display, fall back to raw value (v)
|
|
541
540
|
if (cell.w !== undefined)
|
|
542
541
|
return cell.w;
|
|
543
542
|
if (cell.v !== undefined)
|
|
@@ -551,10 +550,14 @@ export class TabularDriver {
|
|
|
551
550
|
}
|
|
552
551
|
return vals;
|
|
553
552
|
};
|
|
554
|
-
//
|
|
555
|
-
|
|
553
|
+
// ✅ FIX: headerRow is the user's 0-based index counting from
|
|
554
|
+
// the first row of the file. Do NOT add startRow — the user
|
|
555
|
+
// sees row 0 as the first row regardless of where the sheet
|
|
556
|
+
// range begins. We simply use headerRow as the absolute sheet
|
|
557
|
+
// row index.
|
|
558
|
+
const headerSheetRow = headerRow;
|
|
556
559
|
console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}, startCol=${startCol}, endCol=${endCol}`);
|
|
557
|
-
console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow}`);
|
|
560
|
+
console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow} (absolute, no startRow offset)`);
|
|
558
561
|
if (headerSheetRow > endRow) {
|
|
559
562
|
console.warn(`[TabularDriver] headerRow ${headerRow} (sheet row ${headerSheetRow}) exceeds endRow ${endRow}`);
|
|
560
563
|
processedSheets++;
|
|
@@ -664,7 +664,6 @@ export class TabularDriver {
|
|
|
664
664
|
const addr = XLSX.utils.encode_cell({ r, c });
|
|
665
665
|
const cell = worksheet[addr];
|
|
666
666
|
if (!cell) return null;
|
|
667
|
-
// Prefer formatted string (w) for display, fall back to raw value (v)
|
|
668
667
|
if (cell.w !== undefined) return cell.w;
|
|
669
668
|
if (cell.v !== undefined) return cell.v;
|
|
670
669
|
return null;
|
|
@@ -678,11 +677,15 @@ export class TabularDriver {
|
|
|
678
677
|
return vals;
|
|
679
678
|
};
|
|
680
679
|
|
|
681
|
-
//
|
|
682
|
-
|
|
680
|
+
// ✅ FIX: headerRow is the user's 0-based index counting from
|
|
681
|
+
// the first row of the file. Do NOT add startRow — the user
|
|
682
|
+
// sees row 0 as the first row regardless of where the sheet
|
|
683
|
+
// range begins. We simply use headerRow as the absolute sheet
|
|
684
|
+
// row index.
|
|
685
|
+
const headerSheetRow = headerRow;
|
|
683
686
|
|
|
684
687
|
console.log(`[TabularDriver] Sheet "${sheetName}": range=${ref}, startRow=${startRow}, endRow=${endRow}, startCol=${startCol}, endCol=${endCol}`);
|
|
685
|
-
console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow}`);
|
|
688
|
+
console.log(`[TabularDriver] headerRow=${headerRow}, headerSheetRow=${headerSheetRow} (absolute, no startRow offset)`);
|
|
686
689
|
|
|
687
690
|
if (headerSheetRow > endRow) {
|
|
688
691
|
console.warn(`[TabularDriver] headerRow ${headerRow} (sheet row ${headerSheetRow}) exceeds endRow ${endRow}`);
|