juxscript 1.1.205 → 1.1.207
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 +7 -2
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +185 -243
- package/lib/components/dataframe.ts +203 -255
- package/lib/storage/TabularDriver.d.ts +10 -4
- package/lib/storage/TabularDriver.d.ts.map +1 -1
- package/lib/storage/TabularDriver.js +61 -23
- package/lib/storage/TabularDriver.ts +57 -23
- package/package.json +1 -1
|
@@ -87,11 +87,16 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
87
87
|
private _updateStatus;
|
|
88
88
|
private _setDataFrame;
|
|
89
89
|
private _detectMalformedData;
|
|
90
|
-
private _detectLikelyHeaderRow;
|
|
91
90
|
private _showReshapeModal;
|
|
92
91
|
private _cleanupReshapeModal;
|
|
93
|
-
private _showExcelReshapeModal;
|
|
94
92
|
private _escapeHtml;
|
|
93
|
+
/**
|
|
94
|
+
* Build a clickable preview table from raw row data.
|
|
95
|
+
* Each row stores its actual sheet row index via data-sheet-row attribute.
|
|
96
|
+
* Returns the table HTML string.
|
|
97
|
+
*/
|
|
98
|
+
private _buildClickablePreviewHTML;
|
|
99
|
+
private _showExcelReshapeModal;
|
|
95
100
|
private _showCSVReshapeModal;
|
|
96
101
|
update(_prop: string, _value: any): void;
|
|
97
102
|
render(targetId?: string | HTMLElement | BaseComponent<any>): this;
|
|
@@ -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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"}
|
|
@@ -416,29 +416,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
416
416
|
}
|
|
417
417
|
return false;
|
|
418
418
|
}
|
|
419
|
-
_detectLikelyHeaderRow(df) {
|
|
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;
|
|
425
|
-
for (let i = 0; i < Math.min(rows.length, 10); i++) {
|
|
426
|
-
const row = rows[i];
|
|
427
|
-
const values = Object.values(row);
|
|
428
|
-
const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
|
|
429
|
-
if (nonEmpty.length < values.length * 0.5)
|
|
430
|
-
continue;
|
|
431
|
-
const nonNumericCount = nonEmpty.filter(v => {
|
|
432
|
-
const str = String(v).trim();
|
|
433
|
-
return isNaN(Number(str)) && str !== '';
|
|
434
|
-
}).length;
|
|
435
|
-
if (nonNumericCount >= nonEmpty.length * 0.7) {
|
|
436
|
-
// toRows index i = file row (i + 1) since row 0 was used as headers
|
|
437
|
-
return i + 1;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
return 0;
|
|
441
|
-
}
|
|
442
419
|
/* ═══════════════════════════════════════════════════
|
|
443
420
|
* RESHAPE MODAL
|
|
444
421
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -461,24 +438,92 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
461
438
|
this._reshapeModalRendered = false;
|
|
462
439
|
}
|
|
463
440
|
}
|
|
441
|
+
_escapeHtml(text) {
|
|
442
|
+
const div = document.createElement('div');
|
|
443
|
+
div.textContent = text;
|
|
444
|
+
return div.innerHTML;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Build a clickable preview table from raw row data.
|
|
448
|
+
* Each row stores its actual sheet row index via data-sheet-row attribute.
|
|
449
|
+
* Returns the table HTML string.
|
|
450
|
+
*/
|
|
451
|
+
_buildClickablePreviewHTML(rawRows, selectedSheetRow) {
|
|
452
|
+
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 12px;">';
|
|
453
|
+
for (const { sheetRow, values } of rawRows) {
|
|
454
|
+
const isHeader = (sheetRow === selectedSheetRow);
|
|
455
|
+
const isSkipped = (sheetRow < selectedSheetRow);
|
|
456
|
+
let rowStyle = 'border-bottom: 1px solid hsl(var(--border)); cursor: pointer; transition: background 0.1s;';
|
|
457
|
+
if (isHeader) {
|
|
458
|
+
rowStyle += 'background: hsl(142 71% 45% / 0.15); font-weight: 600;';
|
|
459
|
+
}
|
|
460
|
+
else if (isSkipped) {
|
|
461
|
+
rowStyle += 'background: hsl(var(--muted) / 0.4); color: hsl(var(--muted-foreground)); font-style: italic; opacity: 0.7;';
|
|
462
|
+
}
|
|
463
|
+
html += `<tr data-sheet-row="${sheetRow}" style="${rowStyle}" onmouseover="this.style.outline='2px solid hsl(142 71% 45% / 0.5)'" onmouseout="this.style.outline=''">`;
|
|
464
|
+
// Row index cell
|
|
465
|
+
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; user-select: none;">`;
|
|
466
|
+
if (isHeader) {
|
|
467
|
+
html += `<span style="color: hsl(142 71% 45%);">▶ ${sheetRow}</span>`;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
html += `${sheetRow}`;
|
|
471
|
+
}
|
|
472
|
+
html += '</td>';
|
|
473
|
+
// Show first 6 columns
|
|
474
|
+
const displayCols = values.slice(0, 6);
|
|
475
|
+
displayCols.forEach(val => {
|
|
476
|
+
const displayVal = val != null ? String(val).substring(0, 20) : '';
|
|
477
|
+
const cellStyle = isHeader
|
|
478
|
+
? 'padding: 8px 12px; font-weight: 600; color: hsl(var(--foreground));'
|
|
479
|
+
: 'padding: 8px 12px;';
|
|
480
|
+
html += `<td style="${cellStyle}">${this._escapeHtml(displayVal)}</td>`;
|
|
481
|
+
});
|
|
482
|
+
if (values.length > 6) {
|
|
483
|
+
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));">…</td>`;
|
|
484
|
+
}
|
|
485
|
+
// Status badge
|
|
486
|
+
html += `<td style="padding: 8px 12px; text-align: right; white-space: nowrap; user-select: none;">`;
|
|
487
|
+
if (isHeader) {
|
|
488
|
+
html += '<span style="background: hsl(142 71% 45%); color: white; padding: 3px 8px; border-radius: 4px; font-size: 10px; font-weight: 600;">HEADER</span>';
|
|
489
|
+
}
|
|
490
|
+
else if (isSkipped) {
|
|
491
|
+
html += '<span style="color: hsl(var(--muted-foreground)); font-size: 10px;">skipped</span>';
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
html += '<span style="color: hsl(var(--muted-foreground)); font-size: 10px;">data</span>';
|
|
495
|
+
}
|
|
496
|
+
html += '</td></tr>';
|
|
497
|
+
}
|
|
498
|
+
html += '</table>';
|
|
499
|
+
return html;
|
|
500
|
+
}
|
|
464
501
|
async _showExcelReshapeModal() {
|
|
465
502
|
if (!this._rawFileData?.file)
|
|
466
503
|
return;
|
|
467
504
|
this._cleanupReshapeModal();
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
505
|
+
// ✅ Use the SAME cell-reading method as the parser
|
|
506
|
+
const rawRows = await this._driver.readRawExcelRows(this._rawFileData.file, 15);
|
|
507
|
+
if (rawRows.length === 0)
|
|
508
|
+
return;
|
|
509
|
+
// Log what we got so we can verify alignment
|
|
510
|
+
console.log('[DataFrame Preview] Raw rows from readRawExcelRows:');
|
|
511
|
+
rawRows.forEach(r => console.log(` sheetRow ${r.sheetRow}:`, r.values.slice(0, 5)));
|
|
512
|
+
// Auto-detect best header row
|
|
513
|
+
let selectedSheetRow = rawRows[0].sheetRow;
|
|
514
|
+
for (const { sheetRow, values } of rawRows) {
|
|
515
|
+
const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
|
|
516
|
+
if (nonEmpty.length < values.length * 0.5)
|
|
517
|
+
continue;
|
|
518
|
+
const nonNumeric = nonEmpty.filter(v => {
|
|
519
|
+
const str = String(v).trim();
|
|
520
|
+
return isNaN(Number(str)) && str !== '';
|
|
521
|
+
}).length;
|
|
522
|
+
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
523
|
+
selectedSheetRow = sheetRow;
|
|
524
|
+
break;
|
|
477
525
|
}
|
|
478
526
|
}
|
|
479
|
-
catch {
|
|
480
|
-
suggestedRow = 0;
|
|
481
|
-
}
|
|
482
527
|
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
483
528
|
title: 'Excel Import Settings',
|
|
484
529
|
size: 'large',
|
|
@@ -487,14 +532,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
487
532
|
});
|
|
488
533
|
const modalContentHTML = `
|
|
489
534
|
<div style="margin-bottom: 1rem;">
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
</label>
|
|
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>
|
|
535
|
+
<div id="${this._id}-reshape-hint" style="padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;"></div>
|
|
536
|
+
<input type="hidden" id="${this._id}-header-row" value="${selectedSheetRow}" />
|
|
495
537
|
</div>
|
|
496
|
-
<div
|
|
497
|
-
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
538
|
+
<div>
|
|
539
|
+
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Click a row to select it as the header:</div>
|
|
498
540
|
<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>
|
|
499
541
|
</div>
|
|
500
542
|
`;
|
|
@@ -512,6 +554,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
512
554
|
click: async () => {
|
|
513
555
|
const input = document.getElementById(`${this._id}-header-row`);
|
|
514
556
|
const headerRow = parseInt(input.value) || 0;
|
|
557
|
+
console.log(`[DataFrame] Apply clicked: headerRow=${headerRow}`);
|
|
515
558
|
this.state.loading = true;
|
|
516
559
|
this._updateStatus('Re-parsing with new settings...', 'loading');
|
|
517
560
|
try {
|
|
@@ -545,129 +588,73 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
545
588
|
this._reshapeModal.render(document.body);
|
|
546
589
|
this._reshapeModalRendered = true;
|
|
547
590
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
548
|
-
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
549
591
|
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
550
592
|
const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
|
|
551
|
-
const
|
|
593
|
+
const hiddenInput = document.getElementById(`${this._id}-header-row`);
|
|
594
|
+
const updateHint = (row) => {
|
|
552
595
|
if (!hintDiv)
|
|
553
596
|
return;
|
|
554
|
-
|
|
555
|
-
|
|
597
|
+
const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
|
|
598
|
+
const headerNames = vals.filter((v) => v != null && String(v).trim() !== '').map((v) => String(v).trim());
|
|
599
|
+
const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
|
|
600
|
+
if (row > rawRows[0].sheetRow) {
|
|
601
|
+
hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
|
|
556
602
|
}
|
|
557
603
|
else {
|
|
558
|
-
hintDiv.innerHTML = `
|
|
604
|
+
hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
|
|
559
605
|
}
|
|
560
606
|
};
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
607
|
+
const renderPreview = (selected) => {
|
|
608
|
+
if (!previewDiv)
|
|
609
|
+
return;
|
|
610
|
+
previewDiv.innerHTML = this._buildClickablePreviewHTML(rawRows, selected);
|
|
611
|
+
previewDiv.querySelectorAll('tr[data-sheet-row]').forEach(tr => {
|
|
612
|
+
tr.addEventListener('click', () => {
|
|
613
|
+
const rowIdx = parseInt(tr.dataset.sheetRow);
|
|
614
|
+
hiddenInput.value = String(rowIdx);
|
|
615
|
+
console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
|
|
616
|
+
updateHint(rowIdx);
|
|
617
|
+
renderPreview(rowIdx);
|
|
572
618
|
});
|
|
573
|
-
|
|
574
|
-
const worksheet = workbook.Sheets[sheetName];
|
|
575
|
-
const ref = worksheet['!ref'];
|
|
576
|
-
if (!ref) {
|
|
577
|
-
if (previewDiv)
|
|
578
|
-
previewDiv.textContent = 'No data found';
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
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
|
-
};
|
|
603
|
-
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 12px;">';
|
|
604
|
-
const totalRowsToShow = Math.min(headerRow + 8, endRow + 1);
|
|
605
|
-
for (let fileRow = 0; fileRow < totalRowsToShow; fileRow++) {
|
|
606
|
-
const isHeader = (fileRow === headerRow);
|
|
607
|
-
const isSkipped = (fileRow < headerRow);
|
|
608
|
-
let rowStyle = 'border-bottom: 1px solid hsl(var(--border));';
|
|
609
|
-
if (isHeader) {
|
|
610
|
-
rowStyle += 'background: hsl(142 71% 45% / 0.15); font-weight: 600;';
|
|
611
|
-
}
|
|
612
|
-
else if (isSkipped) {
|
|
613
|
-
rowStyle += 'background: hsl(var(--muted) / 0.4); color: hsl(var(--muted-foreground)); font-style: italic; opacity: 0.7;';
|
|
614
|
-
}
|
|
615
|
-
html += `<tr style="${rowStyle}">`;
|
|
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;">`;
|
|
617
|
-
if (isHeader) {
|
|
618
|
-
html += `<span style="color: hsl(142 71% 45%);">▶ ${fileRow}</span>`;
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
html += `${fileRow}`;
|
|
622
|
-
}
|
|
623
|
-
html += '</td>';
|
|
624
|
-
const values = readRow(fileRow);
|
|
625
|
-
const displayCols = values.slice(0, 6);
|
|
626
|
-
displayCols.forEach(val => {
|
|
627
|
-
const displayVal = val != null ? String(val).substring(0, 20) : '';
|
|
628
|
-
const cellStyle = isHeader
|
|
629
|
-
? 'padding: 8px 12px; font-weight: 600; color: hsl(var(--foreground));'
|
|
630
|
-
: 'padding: 8px 12px;';
|
|
631
|
-
html += `<td style="${cellStyle}">${this._escapeHtml(displayVal)}</td>`;
|
|
632
|
-
});
|
|
633
|
-
if (values.length > 6) {
|
|
634
|
-
html += `<td style="padding: 8px 12px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
635
|
-
}
|
|
636
|
-
html += `<td style="padding: 8px 12px; text-align: right; white-space: nowrap;">`;
|
|
637
|
-
if (isHeader) {
|
|
638
|
-
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
639
|
-
}
|
|
640
|
-
else if (isSkipped) {
|
|
641
|
-
html += '<span style="color: hsl(var(--muted-foreground));">skipped</span>';
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
645
|
-
}
|
|
646
|
-
html += '</td></tr>';
|
|
647
|
-
}
|
|
648
|
-
html += '</table>';
|
|
649
|
-
if (previewDiv)
|
|
650
|
-
previewDiv.innerHTML = html;
|
|
651
|
-
}
|
|
652
|
-
catch (err) {
|
|
653
|
-
if (previewDiv)
|
|
654
|
-
previewDiv.textContent = `Error: ${err.message}`;
|
|
655
|
-
}
|
|
619
|
+
});
|
|
656
620
|
};
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
updatePreview();
|
|
621
|
+
updateHint(selectedSheetRow);
|
|
622
|
+
renderPreview(selectedSheetRow);
|
|
660
623
|
this._reshapeModal.open();
|
|
661
624
|
}
|
|
662
|
-
_escapeHtml(text) {
|
|
663
|
-
const div = document.createElement('div');
|
|
664
|
-
div.textContent = text;
|
|
665
|
-
return div.innerHTML;
|
|
666
|
-
}
|
|
667
625
|
_showCSVReshapeModal() {
|
|
668
|
-
if (!this._rawFileData)
|
|
626
|
+
if (!this._rawFileData?.text)
|
|
669
627
|
return;
|
|
670
628
|
this._cleanupReshapeModal();
|
|
629
|
+
const text = this._rawFileData.text;
|
|
630
|
+
const detected = this._driver._detectDelimiter(text);
|
|
631
|
+
// Parse raw lines
|
|
632
|
+
const lines = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
633
|
+
const rawRows = [];
|
|
634
|
+
const maxPreviewRows = Math.min(lines.length, 15);
|
|
635
|
+
for (let i = 0; i < maxPreviewRows; i++) {
|
|
636
|
+
if (!lines[i]) {
|
|
637
|
+
rawRows.push({ sheetRow: i, values: [''] });
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
const values = this._driver._parseLine(lines[i], detected);
|
|
641
|
+
rawRows.push({ sheetRow: i, values });
|
|
642
|
+
}
|
|
643
|
+
// Auto-detect header row
|
|
644
|
+
let selectedRow = 0;
|
|
645
|
+
for (const { sheetRow, values } of rawRows) {
|
|
646
|
+
const nonEmpty = values.filter((v) => v.trim() !== '');
|
|
647
|
+
if (nonEmpty.length < values.length * 0.5)
|
|
648
|
+
continue;
|
|
649
|
+
const nonNumeric = nonEmpty.filter((v) => {
|
|
650
|
+
const trimmed = v.trim();
|
|
651
|
+
return isNaN(Number(trimmed)) && trimmed !== '';
|
|
652
|
+
}).length;
|
|
653
|
+
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
654
|
+
selectedRow = sheetRow;
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
671
658
|
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
672
659
|
title: 'CSV Import Settings',
|
|
673
660
|
size: 'large',
|
|
@@ -685,12 +672,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
685
672
|
</select>
|
|
686
673
|
</div>
|
|
687
674
|
<div style="margin-bottom: 1rem;">
|
|
688
|
-
<
|
|
689
|
-
<input type="
|
|
675
|
+
<div id="${this._id}-reshape-hint" style="padding: 0.75rem; background: hsl(var(--muted) / 0.5); border-radius: var(--radius); font-size: 0.875rem;"></div>
|
|
676
|
+
<input type="hidden" id="${this._id}-header-row" value="${selectedRow}" />
|
|
690
677
|
</div>
|
|
691
|
-
<div
|
|
692
|
-
|
|
693
|
-
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
|
|
678
|
+
<div>
|
|
679
|
+
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Click a row to select it as the header:</div>
|
|
694
680
|
<div id="${this._id}-preview" style="font-family: 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>
|
|
695
681
|
</div>
|
|
696
682
|
`;
|
|
@@ -706,12 +692,10 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
706
692
|
label: 'Apply & Re-import',
|
|
707
693
|
variant: 'primary',
|
|
708
694
|
click: async () => {
|
|
709
|
-
if (!this._rawFileData?.text)
|
|
710
|
-
return;
|
|
711
695
|
const delimiterSelect = document.getElementById(`${this._id}-delimiter`);
|
|
712
|
-
const
|
|
696
|
+
const hiddenInput = document.getElementById(`${this._id}-header-row`);
|
|
713
697
|
const delim = delimiterSelect.value;
|
|
714
|
-
const headerRow = parseInt(
|
|
698
|
+
const headerRow = parseInt(hiddenInput.value) || 0;
|
|
715
699
|
this.state.loading = true;
|
|
716
700
|
this._updateStatus('Re-parsing with new settings...', 'loading');
|
|
717
701
|
try {
|
|
@@ -735,102 +719,60 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
735
719
|
this._reshapeModalRendered = true;
|
|
736
720
|
requestAnimationFrame(() => {
|
|
737
721
|
const delimiterSelect = document.getElementById(`${this._id}-delimiter`);
|
|
738
|
-
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
739
722
|
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
740
723
|
const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const detectedHeaderRow = this._driver._detectHeaderRow(this._rawFileData.text, detected);
|
|
746
|
-
if (headerRowInput)
|
|
747
|
-
headerRowInput.value = String(detectedHeaderRow);
|
|
748
|
-
}
|
|
749
|
-
const updateHint = () => {
|
|
724
|
+
const hiddenInput = document.getElementById(`${this._id}-header-row`);
|
|
725
|
+
if (delimiterSelect)
|
|
726
|
+
delimiterSelect.value = detected;
|
|
727
|
+
const updateHint = (row) => {
|
|
750
728
|
if (!hintDiv)
|
|
751
729
|
return;
|
|
752
|
-
const
|
|
753
|
-
|
|
754
|
-
|
|
730
|
+
const vals = rawRows.find(r => r.sheetRow === row)?.values ?? [];
|
|
731
|
+
const headerNames = vals.filter((v) => v != null && String(v).trim() !== '').map((v) => String(v).trim());
|
|
732
|
+
const preview = headerNames.slice(0, 4).join(', ') + (headerNames.length > 4 ? '…' : '');
|
|
733
|
+
if (row > rawRows[0].sheetRow) {
|
|
734
|
+
hintDiv.innerHTML = `Sheet row <strong>${row}</strong> selected as header. Columns: <code>${this._escapeHtml(preview)}</code>. Rows before it will be skipped.`;
|
|
755
735
|
}
|
|
756
736
|
else {
|
|
757
|
-
hintDiv.innerHTML = `
|
|
737
|
+
hintDiv.innerHTML = `Sheet row <strong>${row}</strong> (first row) selected as header. Columns: <code>${this._escapeHtml(preview)}</code>`;
|
|
758
738
|
}
|
|
759
739
|
};
|
|
760
|
-
const
|
|
761
|
-
if (!this._rawFileData?.text)
|
|
762
|
-
return;
|
|
740
|
+
const reparse = () => {
|
|
763
741
|
const delim = delimiterSelect?.value || ',';
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
headerRow: 0,
|
|
770
|
-
hasHeader: true,
|
|
771
|
-
maxRows: headerRow + 10
|
|
772
|
-
});
|
|
773
|
-
const rawCols = rawDf.columns;
|
|
774
|
-
const rawRows = rawDf.toRows();
|
|
775
|
-
let html = '<table style="width: 100%; border-collapse: collapse; font-size: 11px;">';
|
|
776
|
-
const totalRows = Math.min(headerRow + 8, rawRows.length + 1);
|
|
777
|
-
for (let i = 0; i < totalRows; i++) {
|
|
778
|
-
const isHeader = (i === headerRow);
|
|
779
|
-
const isSkipped = (i < headerRow);
|
|
780
|
-
let rowStyle = 'border-bottom: 1px solid hsl(var(--border));';
|
|
781
|
-
if (isHeader) {
|
|
782
|
-
rowStyle += 'background: hsl(var(--primary) / 0.15); font-weight: bold;';
|
|
783
|
-
}
|
|
784
|
-
else if (isSkipped) {
|
|
785
|
-
rowStyle += 'background: hsl(var(--muted) / 0.3); color: hsl(var(--muted-foreground)); font-style: italic;';
|
|
786
|
-
}
|
|
787
|
-
html += `<tr style="${rowStyle}">`;
|
|
788
|
-
html += `<td style="padding: 6px 8px; width: 50px; color: hsl(var(--muted-foreground)); font-weight: 500;">`;
|
|
789
|
-
html += isHeader ? `<strong>→ ${i}</strong>` : `${i}`;
|
|
790
|
-
html += '</td>';
|
|
791
|
-
let values;
|
|
792
|
-
if (i === 0) {
|
|
793
|
-
values = rawCols;
|
|
794
|
-
}
|
|
795
|
-
else if (i - 1 < rawRows.length) {
|
|
796
|
-
values = Object.values(rawRows[i - 1]);
|
|
797
|
-
}
|
|
798
|
-
else {
|
|
799
|
-
values = [];
|
|
800
|
-
}
|
|
801
|
-
values.slice(0, 6).forEach(val => {
|
|
802
|
-
const displayVal = val != null ? String(val).substring(0, 25) : '';
|
|
803
|
-
html += `<td style="padding: 6px 8px;">${this._escapeHtml(displayVal)}</td>`;
|
|
804
|
-
});
|
|
805
|
-
if (values.length > 6) {
|
|
806
|
-
html += `<td style="padding: 6px 8px; color: hsl(var(--muted-foreground));">...</td>`;
|
|
807
|
-
}
|
|
808
|
-
html += `<td style="padding: 6px 8px; text-align: right; font-size: 10px;">`;
|
|
809
|
-
if (isHeader) {
|
|
810
|
-
html += '<span style="background: hsl(var(--primary)); color: white; padding: 2px 6px; border-radius: 4px;">HEADER</span>';
|
|
811
|
-
}
|
|
812
|
-
else if (isSkipped) {
|
|
813
|
-
html += '<span style="color: hsl(var(--muted-foreground));">skipped</span>';
|
|
814
|
-
}
|
|
815
|
-
else {
|
|
816
|
-
html += '<span style="color: hsl(var(--success));">data</span>';
|
|
817
|
-
}
|
|
818
|
-
html += '</td></tr>';
|
|
742
|
+
rawRows.length = 0;
|
|
743
|
+
for (let i = 0; i < maxPreviewRows; i++) {
|
|
744
|
+
if (!lines[i]) {
|
|
745
|
+
rawRows.push({ sheetRow: i, values: [''] });
|
|
746
|
+
continue;
|
|
819
747
|
}
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
previewDiv.innerHTML = html;
|
|
823
|
-
}
|
|
824
|
-
catch (err) {
|
|
825
|
-
if (previewDiv)
|
|
826
|
-
previewDiv.textContent = `Error: ${err.message}`;
|
|
748
|
+
const values = this._driver._parseLine(lines[i], delim);
|
|
749
|
+
rawRows.push({ sheetRow: i, values });
|
|
827
750
|
}
|
|
828
751
|
};
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
752
|
+
const renderPreview = (selected) => {
|
|
753
|
+
if (!previewDiv)
|
|
754
|
+
return;
|
|
755
|
+
previewDiv.innerHTML = this._buildClickablePreviewHTML(rawRows, selected);
|
|
756
|
+
previewDiv.querySelectorAll('tr[data-sheet-row]').forEach(tr => {
|
|
757
|
+
tr.addEventListener('click', () => {
|
|
758
|
+
const rowIdx = parseInt(tr.dataset.sheetRow);
|
|
759
|
+
hiddenInput.value = String(rowIdx);
|
|
760
|
+
console.log(`[DataFrame Preview] Clicked sheetRow=${rowIdx}`);
|
|
761
|
+
updateHint(rowIdx);
|
|
762
|
+
renderPreview(rowIdx);
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
};
|
|
766
|
+
if (delimiterSelect) {
|
|
767
|
+
delimiterSelect.addEventListener('change', () => {
|
|
768
|
+
reparse();
|
|
769
|
+
const current = parseInt(hiddenInput.value) || 0;
|
|
770
|
+
updateHint(current);
|
|
771
|
+
renderPreview(current);
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
updateHint(selectedRow);
|
|
775
|
+
renderPreview(selectedRow);
|
|
834
776
|
this._reshapeModal.open();
|
|
835
777
|
});
|
|
836
778
|
}
|