juxscript 1.1.191 → 1.1.193
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":"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;AAQnC,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,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,SAAS,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,aAAa,CAAC,cAAc,CAAC;IACjE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,mBAAmB,CAAiB;IAC5C,OAAO,CAAC,YAAY,CAAiE;IACrF,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,qBAAqB,CAAkB;gBAEnC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAmCtD,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;IAWpC,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;IAC5B,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC7B,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/B,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;YAMf,WAAW;IAyDzB,OAAO,CAAC,iBAAiB;IAyGzB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,oBAAoB;YASd,sBAAsB;IA4IpC,OAAO,CAAC,oBAAoB;IAgK5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAExC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAoErE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,CAExF"}
|
|
@@ -360,8 +360,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
360
360
|
this._table.columns(columnDefs).rows(this._df.toRows());
|
|
361
361
|
}
|
|
362
362
|
const isMalformed = this._detectMalformedData(this._df);
|
|
363
|
-
if (isMalformed && this.
|
|
364
|
-
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may
|
|
363
|
+
if (isMalformed && this._rawFileData) {
|
|
364
|
+
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may need reformatting)`, 'warning');
|
|
365
365
|
requestAnimationFrame(() => {
|
|
366
366
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
367
367
|
if (statusEl) {
|
|
@@ -376,12 +376,13 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
376
376
|
}
|
|
377
377
|
else {
|
|
378
378
|
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
|
|
379
|
-
if
|
|
379
|
+
// Always show settings button if we have raw file data
|
|
380
|
+
if (this._rawFileData) {
|
|
380
381
|
requestAnimationFrame(() => {
|
|
381
382
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
382
383
|
if (statusEl) {
|
|
383
384
|
const settingsBtn = document.createElement('button');
|
|
384
|
-
settingsBtn.textContent = 'Settings';
|
|
385
|
+
settingsBtn.textContent = 'Import Settings';
|
|
385
386
|
settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
|
|
386
387
|
settingsBtn.style.marginLeft = '0.5rem';
|
|
387
388
|
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
@@ -430,13 +431,13 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
430
431
|
return isNaN(Number(str)) && str !== '';
|
|
431
432
|
}).length;
|
|
432
433
|
if (nonNumericCount >= nonEmpty.length * 0.7 && i > 0) {
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
return i +
|
|
434
|
+
// i is the index in toRows(). Row 0 of the file was already consumed
|
|
435
|
+
// as the header during the initial parse, so the actual 0-based file
|
|
436
|
+
// row index is i + 1.
|
|
437
|
+
return i + 1;
|
|
437
438
|
}
|
|
438
439
|
}
|
|
439
|
-
return
|
|
440
|
+
return 0;
|
|
440
441
|
}
|
|
441
442
|
/* ═══════════════════════════════════════════════════
|
|
442
443
|
* RESHAPE MODAL
|
|
@@ -464,7 +465,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
464
465
|
if (!this._rawFileData?.file)
|
|
465
466
|
return;
|
|
466
467
|
this._cleanupReshapeModal();
|
|
467
|
-
const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) :
|
|
468
|
+
const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 0;
|
|
468
469
|
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
469
470
|
title: 'Excel Import Settings',
|
|
470
471
|
size: 'large',
|
|
@@ -481,14 +482,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
481
482
|
id="${this._id}-header-row"
|
|
482
483
|
class="jux-input-element"
|
|
483
484
|
value="${suggestedRow}"
|
|
484
|
-
min="
|
|
485
|
+
min="0"
|
|
485
486
|
max="50"
|
|
486
487
|
style="width: 100%;"
|
|
487
488
|
/>
|
|
488
|
-
<div class="jux-reshape-hint">
|
|
489
|
-
<strong>Detected issue:</strong> The current header row appears to contain metadata or empty values.
|
|
490
|
-
Row ${suggestedRow} looks like it contains the actual column headers.
|
|
491
|
-
Adjust the value above and check the preview below.
|
|
489
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint">
|
|
492
490
|
</div>
|
|
493
491
|
</div>
|
|
494
492
|
<div class="jux-reshape-preview-container">
|
|
@@ -511,8 +509,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
511
509
|
variant: 'primary',
|
|
512
510
|
click: async () => {
|
|
513
511
|
const input = document.getElementById(`${this._id}-header-row`);
|
|
514
|
-
const
|
|
515
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
|
|
512
|
+
const headerRow = parseInt(input.value) || 0;
|
|
516
513
|
this.state.loading = true;
|
|
517
514
|
this._updateStatus('Re-parsing with new settings...', 'loading');
|
|
518
515
|
try {
|
|
@@ -527,7 +524,6 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
527
524
|
this._renderMultiSheet(sheets, this._rawFileData.file.name);
|
|
528
525
|
}
|
|
529
526
|
else {
|
|
530
|
-
this._showReshapeWarning = false;
|
|
531
527
|
this._setDataFrame(sheets[sheetNames[0]], this._rawFileData.file.name);
|
|
532
528
|
}
|
|
533
529
|
this._reshapeModal.closeModal();
|
|
@@ -543,9 +539,16 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
543
539
|
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
544
540
|
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
545
541
|
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
542
|
+
const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
|
|
543
|
+
const updateHint = (headerRow) => {
|
|
544
|
+
if (!hintDiv)
|
|
545
|
+
return;
|
|
546
|
+
hintDiv.innerHTML = `The data starting at <strong>row ${headerRow}</strong> will be used as column headers. ` +
|
|
547
|
+
`Rows before it will be skipped. The preview below shows the row index as the first column.`;
|
|
548
|
+
};
|
|
546
549
|
const updatePreview = async () => {
|
|
547
|
-
const
|
|
548
|
-
|
|
550
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
551
|
+
updateHint(headerRow);
|
|
549
552
|
try {
|
|
550
553
|
const sheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
551
554
|
headerRow,
|
|
@@ -557,12 +560,18 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
557
560
|
previewDiv.textContent = 'No data found';
|
|
558
561
|
return;
|
|
559
562
|
}
|
|
560
|
-
const
|
|
561
|
-
|
|
562
|
-
|
|
563
|
+
const dataRows = firstSheet.toRows().slice(0, 10);
|
|
564
|
+
const idxWidth = 6;
|
|
565
|
+
const colWidth = 22;
|
|
566
|
+
const headerLine = 'Idx'.padEnd(idxWidth) + firstSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
567
|
+
const separator = '─'.repeat(Math.min(headerLine.length, 140));
|
|
568
|
+
const preview = dataRows.map((row, i) => {
|
|
569
|
+
const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
|
|
570
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
571
|
+
return `${rowIdx}${cols}`;
|
|
563
572
|
}).join('\n');
|
|
564
573
|
if (previewDiv) {
|
|
565
|
-
previewDiv.textContent =
|
|
574
|
+
previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
|
|
566
575
|
}
|
|
567
576
|
}
|
|
568
577
|
catch (err) {
|
|
@@ -597,13 +606,14 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
597
606
|
</div>
|
|
598
607
|
<div style="margin-bottom: 1rem;">
|
|
599
608
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Header Row</label>
|
|
600
|
-
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="
|
|
609
|
+
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
|
|
601
610
|
</div>
|
|
602
611
|
<div style="margin-bottom: 1rem;">
|
|
603
612
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
|
|
604
613
|
<input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
|
|
605
614
|
</div>
|
|
606
|
-
<div class="jux-reshape-
|
|
615
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
|
|
616
|
+
<div class="jux-reshape-preview-container" style="margin-top: 1rem;">
|
|
607
617
|
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
|
|
608
618
|
<div id="${this._id}-preview" class="jux-reshape-preview"></div>
|
|
609
619
|
</div>
|
|
@@ -626,8 +636,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
626
636
|
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
627
637
|
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
|
|
628
638
|
const delim = delimiterSelect.value;
|
|
629
|
-
const
|
|
630
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based
|
|
639
|
+
const headerRow = parseInt(headerRowInput.value) || 0;
|
|
631
640
|
const skipRows = parseInt(skipRowsInput.value) || 0;
|
|
632
641
|
this.state.loading = true;
|
|
633
642
|
this._updateStatus('Re-parsing with new settings...', 'loading');
|
|
@@ -636,11 +645,9 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
636
645
|
delimiter: delim,
|
|
637
646
|
headerRow,
|
|
638
647
|
skipRows,
|
|
639
|
-
hasHeader: true
|
|
640
|
-
maxRows: 10
|
|
648
|
+
hasHeader: true
|
|
641
649
|
});
|
|
642
650
|
await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
|
|
643
|
-
this._showReshapeWarning = false;
|
|
644
651
|
this._setDataFrame(df, this._rawFileData.file.name);
|
|
645
652
|
this._reshapeModal.closeModal();
|
|
646
653
|
}
|
|
@@ -657,21 +664,31 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
657
664
|
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
658
665
|
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
|
|
659
666
|
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
667
|
+
const hintDiv = document.getElementById(`${this._id}-reshape-hint`);
|
|
660
668
|
if (this._rawFileData?.text) {
|
|
661
669
|
const detected = this._driver._detectDelimiter(this._rawFileData.text);
|
|
662
670
|
if (delimiterSelect)
|
|
663
671
|
delimiterSelect.value = detected;
|
|
664
672
|
const detectedHeaderRow = this._driver._detectHeaderRow(this._rawFileData.text, detected);
|
|
665
673
|
if (headerRowInput)
|
|
666
|
-
headerRowInput.value = String(detectedHeaderRow
|
|
674
|
+
headerRowInput.value = String(detectedHeaderRow);
|
|
667
675
|
}
|
|
676
|
+
const updateHint = () => {
|
|
677
|
+
if (!hintDiv)
|
|
678
|
+
return;
|
|
679
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
680
|
+
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
681
|
+
hintDiv.innerHTML = `Using <strong>row ${headerRow}</strong> as column headers` +
|
|
682
|
+
(skipRows > 0 ? ` (skipping ${skipRows} rows before it)` : '') +
|
|
683
|
+
`. The Idx column shows the file row index.`;
|
|
684
|
+
};
|
|
668
685
|
const updatePreview = () => {
|
|
669
686
|
if (!this._rawFileData?.text)
|
|
670
687
|
return;
|
|
671
688
|
const delim = delimiterSelect?.value || ',';
|
|
672
|
-
const
|
|
673
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based
|
|
689
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
674
690
|
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
691
|
+
updateHint();
|
|
675
692
|
try {
|
|
676
693
|
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
677
694
|
delimiter: delim,
|
|
@@ -680,12 +697,18 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
680
697
|
hasHeader: true,
|
|
681
698
|
maxRows: 10
|
|
682
699
|
});
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
700
|
+
const dataRows = df.toRows();
|
|
701
|
+
const idxWidth = 6;
|
|
702
|
+
const colWidth = 22;
|
|
703
|
+
const headerLine = 'Idx'.padEnd(idxWidth) + df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
704
|
+
const separator = '─'.repeat(Math.min(headerLine.length, 140));
|
|
705
|
+
const preview = dataRows.map((row, i) => {
|
|
706
|
+
const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(idxWidth);
|
|
707
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
708
|
+
return `${rowIdx}${cols}`;
|
|
686
709
|
}).join('\n');
|
|
687
710
|
if (previewDiv) {
|
|
688
|
-
previewDiv.textContent =
|
|
711
|
+
previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
|
|
689
712
|
}
|
|
690
713
|
}
|
|
691
714
|
catch (err) {
|
|
@@ -452,9 +452,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
452
452
|
|
|
453
453
|
const isMalformed = this._detectMalformedData(this._df!);
|
|
454
454
|
|
|
455
|
-
if (isMalformed && this.
|
|
455
|
+
if (isMalformed && this._rawFileData) {
|
|
456
456
|
this._updateStatus(
|
|
457
|
-
`${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may
|
|
457
|
+
`${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may need reformatting)`,
|
|
458
458
|
'warning'
|
|
459
459
|
);
|
|
460
460
|
|
|
@@ -475,12 +475,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
475
475
|
'success'
|
|
476
476
|
);
|
|
477
477
|
|
|
478
|
-
if
|
|
478
|
+
// Always show settings button if we have raw file data
|
|
479
|
+
if (this._rawFileData) {
|
|
479
480
|
requestAnimationFrame(() => {
|
|
480
481
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
481
482
|
if (statusEl) {
|
|
482
483
|
const settingsBtn = document.createElement('button');
|
|
483
|
-
settingsBtn.textContent = 'Settings';
|
|
484
|
+
settingsBtn.textContent = 'Import Settings';
|
|
484
485
|
settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
|
|
485
486
|
settingsBtn.style.marginLeft = '0.5rem';
|
|
486
487
|
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
@@ -542,14 +543,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
542
543
|
}).length;
|
|
543
544
|
|
|
544
545
|
if (nonNumericCount >= nonEmpty.length * 0.7 && i > 0) {
|
|
545
|
-
//
|
|
546
|
-
//
|
|
547
|
-
//
|
|
548
|
-
return i +
|
|
546
|
+
// i is the index in toRows(). Row 0 of the file was already consumed
|
|
547
|
+
// as the header during the initial parse, so the actual 0-based file
|
|
548
|
+
// row index is i + 1.
|
|
549
|
+
return i + 1;
|
|
549
550
|
}
|
|
550
551
|
}
|
|
551
552
|
|
|
552
|
-
return
|
|
553
|
+
return 0;
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
/* ═══════════════════════════════════════════════════
|
|
@@ -580,7 +581,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
580
581
|
|
|
581
582
|
this._cleanupReshapeModal();
|
|
582
583
|
|
|
583
|
-
const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) :
|
|
584
|
+
const suggestedRow = this._df ? this._detectLikelyHeaderRow(this._df) : 0;
|
|
584
585
|
|
|
585
586
|
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
586
587
|
title: 'Excel Import Settings',
|
|
@@ -599,14 +600,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
599
600
|
id="${this._id}-header-row"
|
|
600
601
|
class="jux-input-element"
|
|
601
602
|
value="${suggestedRow}"
|
|
602
|
-
min="
|
|
603
|
+
min="0"
|
|
603
604
|
max="50"
|
|
604
605
|
style="width: 100%;"
|
|
605
606
|
/>
|
|
606
|
-
<div class="jux-reshape-hint">
|
|
607
|
-
<strong>Detected issue:</strong> The current header row appears to contain metadata or empty values.
|
|
608
|
-
Row ${suggestedRow} looks like it contains the actual column headers.
|
|
609
|
-
Adjust the value above and check the preview below.
|
|
607
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint">
|
|
610
608
|
</div>
|
|
611
609
|
</div>
|
|
612
610
|
<div class="jux-reshape-preview-container">
|
|
@@ -630,8 +628,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
630
628
|
variant: 'primary',
|
|
631
629
|
click: async () => {
|
|
632
630
|
const input = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
|
|
633
|
-
const
|
|
634
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based for driver
|
|
631
|
+
const headerRow = parseInt(input.value) || 0;
|
|
635
632
|
|
|
636
633
|
this.state.loading = true;
|
|
637
634
|
this._updateStatus('Re-parsing with new settings...', 'loading');
|
|
@@ -649,7 +646,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
649
646
|
if (sheetNames.length > 1) {
|
|
650
647
|
this._renderMultiSheet(sheets, this._rawFileData!.file.name);
|
|
651
648
|
} else {
|
|
652
|
-
this._showReshapeWarning = false;
|
|
653
649
|
this._setDataFrame(sheets[sheetNames[0]], this._rawFileData!.file.name);
|
|
654
650
|
}
|
|
655
651
|
|
|
@@ -668,10 +664,19 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
668
664
|
|
|
669
665
|
const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
|
|
670
666
|
const previewDiv = document.getElementById(`${this._id}-preview`)!;
|
|
667
|
+
const hintDiv = document.getElementById(`${this._id}-reshape-hint`)!;
|
|
668
|
+
|
|
669
|
+
const updateHint = (headerRow: number) => {
|
|
670
|
+
if (!hintDiv) return;
|
|
671
|
+
hintDiv.innerHTML = `The data starting at <strong>row ${headerRow}</strong> will be used as column headers. ` +
|
|
672
|
+
`Rows before it will be skipped. The preview below shows the row index as the first column.`;
|
|
673
|
+
};
|
|
671
674
|
|
|
672
675
|
const updatePreview = async () => {
|
|
673
|
-
const
|
|
674
|
-
|
|
676
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
677
|
+
|
|
678
|
+
updateHint(headerRow);
|
|
679
|
+
|
|
675
680
|
try {
|
|
676
681
|
const sheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
|
|
677
682
|
headerRow,
|
|
@@ -684,13 +689,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
684
689
|
return;
|
|
685
690
|
}
|
|
686
691
|
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
692
|
+
const dataRows = firstSheet.toRows().slice(0, 10);
|
|
693
|
+
const idxWidth = 6;
|
|
694
|
+
const colWidth = 22;
|
|
695
|
+
|
|
696
|
+
const headerLine = 'Idx'.padEnd(idxWidth) + firstSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
697
|
+
const separator = '─'.repeat(Math.min(headerLine.length, 140));
|
|
698
|
+
|
|
699
|
+
const preview = dataRows.map((row, i) => {
|
|
700
|
+
const rowIdx = String(headerRow + 1 + i).padEnd(idxWidth);
|
|
701
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
702
|
+
return `${rowIdx}${cols}`;
|
|
690
703
|
}).join('\n');
|
|
691
704
|
|
|
692
705
|
if (previewDiv) {
|
|
693
|
-
previewDiv.textContent =
|
|
706
|
+
previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
|
|
694
707
|
}
|
|
695
708
|
} catch (err: any) {
|
|
696
709
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|
|
@@ -727,13 +740,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
727
740
|
</div>
|
|
728
741
|
<div style="margin-bottom: 1rem;">
|
|
729
742
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Header Row</label>
|
|
730
|
-
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="
|
|
743
|
+
<input type="number" id="${this._id}-header-row" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
|
|
731
744
|
</div>
|
|
732
745
|
<div style="margin-bottom: 1rem;">
|
|
733
746
|
<label style="display: block; font-weight: 600; margin-bottom: 0.5rem;">Skip Rows Before Header</label>
|
|
734
747
|
<input type="number" id="${this._id}-skip-rows" class="jux-input-element" value="0" min="0" max="50" style="width: 100%;" />
|
|
735
748
|
</div>
|
|
736
|
-
<div class="jux-reshape-
|
|
749
|
+
<div id="${this._id}-reshape-hint" class="jux-reshape-hint"></div>
|
|
750
|
+
<div class="jux-reshape-preview-container" style="margin-top: 1rem;">
|
|
737
751
|
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">Preview</div>
|
|
738
752
|
<div id="${this._id}-preview" class="jux-reshape-preview"></div>
|
|
739
753
|
</div>
|
|
@@ -758,8 +772,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
758
772
|
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
|
|
759
773
|
|
|
760
774
|
const delim = delimiterSelect.value;
|
|
761
|
-
const
|
|
762
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based
|
|
775
|
+
const headerRow = parseInt(headerRowInput.value) || 0;
|
|
763
776
|
const skipRows = parseInt(skipRowsInput.value) || 0;
|
|
764
777
|
|
|
765
778
|
this.state.loading = true;
|
|
@@ -770,12 +783,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
770
783
|
delimiter: delim,
|
|
771
784
|
headerRow,
|
|
772
785
|
skipRows,
|
|
773
|
-
hasHeader: true
|
|
774
|
-
maxRows: 10
|
|
786
|
+
hasHeader: true
|
|
775
787
|
});
|
|
776
788
|
|
|
777
789
|
await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
|
|
778
|
-
this._showReshapeWarning = false;
|
|
779
790
|
this._setDataFrame(df, this._rawFileData.file.name);
|
|
780
791
|
|
|
781
792
|
this._reshapeModal!.closeModal();
|
|
@@ -794,23 +805,34 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
794
805
|
const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
|
|
795
806
|
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
|
|
796
807
|
const previewDiv = document.getElementById(`${this._id}-preview`)!;
|
|
808
|
+
const hintDiv = document.getElementById(`${this._id}-reshape-hint`)!;
|
|
797
809
|
|
|
798
810
|
if (this._rawFileData?.text) {
|
|
799
811
|
const detected = (this._driver as any)._detectDelimiter(this._rawFileData.text);
|
|
800
812
|
if (delimiterSelect) delimiterSelect.value = detected;
|
|
801
813
|
|
|
802
814
|
const detectedHeaderRow = (this._driver as any)._detectHeaderRow(this._rawFileData.text, detected);
|
|
803
|
-
if (headerRowInput) headerRowInput.value = String(detectedHeaderRow
|
|
815
|
+
if (headerRowInput) headerRowInput.value = String(detectedHeaderRow);
|
|
804
816
|
}
|
|
805
817
|
|
|
818
|
+
const updateHint = () => {
|
|
819
|
+
if (!hintDiv) return;
|
|
820
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
821
|
+
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
822
|
+
hintDiv.innerHTML = `Using <strong>row ${headerRow}</strong> as column headers` +
|
|
823
|
+
(skipRows > 0 ? ` (skipping ${skipRows} rows before it)` : '') +
|
|
824
|
+
`. The Idx column shows the file row index.`;
|
|
825
|
+
};
|
|
826
|
+
|
|
806
827
|
const updatePreview = () => {
|
|
807
828
|
if (!this._rawFileData?.text) return;
|
|
808
829
|
|
|
809
830
|
const delim = delimiterSelect?.value || ',';
|
|
810
|
-
const
|
|
811
|
-
const headerRow = headerRowOneBased - 1; // Convert to 0-based
|
|
831
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
812
832
|
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
813
833
|
|
|
834
|
+
updateHint();
|
|
835
|
+
|
|
814
836
|
try {
|
|
815
837
|
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
816
838
|
delimiter: delim,
|
|
@@ -820,13 +842,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
820
842
|
maxRows: 10
|
|
821
843
|
});
|
|
822
844
|
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
845
|
+
const dataRows = df.toRows();
|
|
846
|
+
const idxWidth = 6;
|
|
847
|
+
const colWidth = 22;
|
|
848
|
+
|
|
849
|
+
const headerLine = 'Idx'.padEnd(idxWidth) + df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
850
|
+
const separator = '─'.repeat(Math.min(headerLine.length, 140));
|
|
851
|
+
|
|
852
|
+
const preview = dataRows.map((row, i) => {
|
|
853
|
+
const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(idxWidth);
|
|
854
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
855
|
+
return `${rowIdx}${cols}`;
|
|
826
856
|
}).join('\n');
|
|
827
857
|
|
|
828
858
|
if (previewDiv) {
|
|
829
|
-
previewDiv.textContent =
|
|
859
|
+
previewDiv.textContent = `${headerLine}\n${separator}\n${preview}`;
|
|
830
860
|
}
|
|
831
861
|
} catch (err: any) {
|
|
832
862
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|