juxscript 1.1.186 → 1.1.187
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.
|
@@ -46,6 +46,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
46
46
|
private _showReshapeWarning;
|
|
47
47
|
private _rawFileData;
|
|
48
48
|
private _reshapeModal;
|
|
49
|
+
private _reshapeModalRendered;
|
|
49
50
|
constructor(id: string, options?: DataFrameOptions);
|
|
50
51
|
protected getTriggerEvents(): readonly string[];
|
|
51
52
|
protected getCallbackEvents(): readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AASnC,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;
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
|
|
@@ -37,6 +37,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
37
37
|
this._showReshapeWarning = true;
|
|
38
38
|
this._rawFileData = null;
|
|
39
39
|
this._reshapeModal = null; // ✅ ADD THIS LINE
|
|
40
|
+
this._reshapeModalRendered = false; // Track if modal already rendered
|
|
40
41
|
this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
|
|
41
42
|
this._showStatus = options.showStatus ?? true;
|
|
42
43
|
this._icon = options.icon ?? '';
|
|
@@ -404,34 +405,38 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
404
405
|
this._table.columns(columnDefs).rows(this._df.toRows());
|
|
405
406
|
}
|
|
406
407
|
// ✅ Detect malformed data
|
|
407
|
-
const isMalformed = this._detectMalformedData(
|
|
408
|
+
const isMalformed = this._detectMalformedData(this._df);
|
|
408
409
|
// ✅ Show warning if malformed
|
|
409
410
|
if (isMalformed && this._showReshapeWarning && this._rawFileData) {
|
|
410
|
-
this._updateStatus(`⚠️ ${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may be malformed)`, 'warning');
|
|
411
|
-
// Add Settings button
|
|
412
|
-
|
|
413
|
-
if (statusEl) {
|
|
414
|
-
const settingsBtn = document.createElement('button');
|
|
415
|
-
settingsBtn.textContent = 'Fix Import Settings';
|
|
416
|
-
settingsBtn.className = 'jux-button jux-button-sm jux-button-warning';
|
|
417
|
-
settingsBtn.style.marginLeft = '0.5rem';
|
|
418
|
-
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
419
|
-
statusEl.appendChild(settingsBtn);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
|
|
424
|
-
// ✅ Still add Settings button for manual adjustment
|
|
425
|
-
if (this._showReshapeWarning && this._rawFileData) {
|
|
411
|
+
this._updateStatus(`⚠️ ${sourceName} — ${this._df.height} rows × ${this._df.width} cols (Data may be malformed — headers may be on wrong row)`, 'warning');
|
|
412
|
+
// Add Fix Import Settings button after a tick to ensure status DOM is ready
|
|
413
|
+
requestAnimationFrame(() => {
|
|
426
414
|
const statusEl = document.getElementById(`${this._id}-status`);
|
|
427
415
|
if (statusEl) {
|
|
428
416
|
const settingsBtn = document.createElement('button');
|
|
429
|
-
settingsBtn.textContent = 'Settings';
|
|
430
|
-
settingsBtn.className = 'jux-button jux-button-sm jux-button-
|
|
417
|
+
settingsBtn.textContent = '⚙️ Fix Import Settings';
|
|
418
|
+
settingsBtn.className = 'jux-button jux-button-sm jux-button-warning';
|
|
431
419
|
settingsBtn.style.marginLeft = '0.5rem';
|
|
432
420
|
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
433
421
|
statusEl.appendChild(settingsBtn);
|
|
434
422
|
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
this._updateStatus(`${sourceName} — ${this._df.height} rows × ${this._df.width} cols`, 'success');
|
|
427
|
+
// ✅ Still add Settings button for manual adjustment
|
|
428
|
+
if (this._showReshapeWarning && this._rawFileData) {
|
|
429
|
+
requestAnimationFrame(() => {
|
|
430
|
+
const statusEl = document.getElementById(`${this._id}-status`);
|
|
431
|
+
if (statusEl) {
|
|
432
|
+
const settingsBtn = document.createElement('button');
|
|
433
|
+
settingsBtn.textContent = '⚙️ Settings';
|
|
434
|
+
settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
|
|
435
|
+
settingsBtn.style.marginLeft = '0.5rem';
|
|
436
|
+
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
437
|
+
statusEl.appendChild(settingsBtn);
|
|
438
|
+
}
|
|
439
|
+
});
|
|
435
440
|
}
|
|
436
441
|
}
|
|
437
442
|
this._triggerCallback('load', this._df, null, this);
|
|
@@ -496,15 +501,21 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
496
501
|
async _showExcelReshapeModal() {
|
|
497
502
|
if (!this._rawFileData?.file)
|
|
498
503
|
return;
|
|
499
|
-
//
|
|
500
|
-
if (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
});
|
|
504
|
+
// Remove old modal from DOM if it was previously rendered
|
|
505
|
+
if (this._reshapeModal && this._reshapeModalRendered) {
|
|
506
|
+
const oldEl = document.getElementById(`${this._id}-reshape-modal`);
|
|
507
|
+
if (oldEl)
|
|
508
|
+
oldEl.remove();
|
|
509
|
+
this._reshapeModal = null;
|
|
510
|
+
this._reshapeModalRendered = false;
|
|
507
511
|
}
|
|
512
|
+
// Create fresh modal
|
|
513
|
+
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
514
|
+
title: 'Excel Import Settings',
|
|
515
|
+
size: 'large',
|
|
516
|
+
close: true,
|
|
517
|
+
backdropClose: false
|
|
518
|
+
});
|
|
508
519
|
// Build modal content
|
|
509
520
|
const modalContent = document.createElement('div');
|
|
510
521
|
modalContent.innerHTML = `
|
|
@@ -561,6 +572,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
561
572
|
this._renderMultiSheet(sheets, this._rawFileData.file.name);
|
|
562
573
|
}
|
|
563
574
|
else {
|
|
575
|
+
this._showReshapeWarning = false; // Prevent recursive warning after manual fix
|
|
564
576
|
this._setDataFrame(sheets[sheetNames[0]], this._rawFileData.file.name);
|
|
565
577
|
}
|
|
566
578
|
this._reshapeModal.closeModal();
|
|
@@ -570,13 +582,17 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
570
582
|
}
|
|
571
583
|
}
|
|
572
584
|
}
|
|
573
|
-
])
|
|
574
|
-
|
|
585
|
+
]);
|
|
586
|
+
// Render modal to document.body and open it
|
|
587
|
+
this._reshapeModal.render(document.body);
|
|
588
|
+
this._reshapeModalRendered = true;
|
|
589
|
+
// Wait a tick for DOM to update after render
|
|
590
|
+
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
575
591
|
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
576
592
|
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
577
593
|
// Update preview on header row change
|
|
578
594
|
const updatePreview = async () => {
|
|
579
|
-
const headerRow = parseInt(headerRowInput
|
|
595
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
580
596
|
try {
|
|
581
597
|
const sheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
582
598
|
headerRow,
|
|
@@ -584,20 +600,24 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
584
600
|
});
|
|
585
601
|
const firstSheet = Object.values(sheets)[0];
|
|
586
602
|
if (!firstSheet) {
|
|
587
|
-
previewDiv
|
|
603
|
+
if (previewDiv)
|
|
604
|
+
previewDiv.textContent = '⚠️ No data found';
|
|
588
605
|
return;
|
|
589
606
|
}
|
|
590
607
|
const preview = firstSheet.toRows().slice(0, 10).map((row, i) => {
|
|
591
608
|
const cols = Object.values(row).map(v => String(v).padEnd(20)).join(' │ ');
|
|
592
609
|
return `${i === 0 ? '📌 ' : ' '}${cols}`;
|
|
593
610
|
}).join('\n');
|
|
594
|
-
|
|
611
|
+
if (previewDiv)
|
|
612
|
+
previewDiv.textContent = `Columns: ${firstSheet.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
595
613
|
}
|
|
596
614
|
catch (err) {
|
|
597
|
-
previewDiv
|
|
615
|
+
if (previewDiv)
|
|
616
|
+
previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
598
617
|
}
|
|
599
618
|
};
|
|
600
|
-
headerRowInput
|
|
619
|
+
if (headerRowInput)
|
|
620
|
+
headerRowInput.addEventListener('input', updatePreview);
|
|
601
621
|
updatePreview();
|
|
602
622
|
this._reshapeModal.open();
|
|
603
623
|
}
|
|
@@ -607,15 +627,21 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
607
627
|
_showCSVReshapeModal() {
|
|
608
628
|
if (!this._rawFileData)
|
|
609
629
|
return;
|
|
610
|
-
//
|
|
611
|
-
if (
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
});
|
|
630
|
+
// Remove old modal from DOM if it was previously rendered
|
|
631
|
+
if (this._reshapeModal && this._reshapeModalRendered) {
|
|
632
|
+
const oldEl = document.getElementById(`${this._id}-reshape-modal`);
|
|
633
|
+
if (oldEl)
|
|
634
|
+
oldEl.remove();
|
|
635
|
+
this._reshapeModal = null;
|
|
636
|
+
this._reshapeModalRendered = false;
|
|
618
637
|
}
|
|
638
|
+
// Create fresh modal
|
|
639
|
+
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
640
|
+
title: 'CSV Import Settings',
|
|
641
|
+
size: 'large',
|
|
642
|
+
close: true,
|
|
643
|
+
backdropClose: false
|
|
644
|
+
});
|
|
619
645
|
// Build modal content
|
|
620
646
|
const modalContent = document.createElement('div');
|
|
621
647
|
modalContent.innerHTML = `
|
|
@@ -674,6 +700,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
674
700
|
hasHeader: true
|
|
675
701
|
});
|
|
676
702
|
await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
|
|
703
|
+
this._showReshapeWarning = false; // Prevent recursive warning after manual fix
|
|
677
704
|
this._setDataFrame(df, this._rawFileData.file.name);
|
|
678
705
|
this._reshapeModal.closeModal();
|
|
679
706
|
}
|
|
@@ -682,49 +709,61 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
682
709
|
}
|
|
683
710
|
}
|
|
684
711
|
}
|
|
685
|
-
])
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
const headerRow = parseInt(headerRowInput.value) || 0;
|
|
704
|
-
const skipRows = parseInt(skipRowsInput.value) || 0;
|
|
705
|
-
try {
|
|
706
|
-
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
707
|
-
delimiter: delim,
|
|
708
|
-
headerRow,
|
|
709
|
-
skipRows,
|
|
710
|
-
hasHeader: true,
|
|
711
|
-
maxRows: 10
|
|
712
|
-
});
|
|
713
|
-
const preview = df.toRows().map((row, i) => {
|
|
714
|
-
const cols = Object.values(row).map(v => String(v).padEnd(20)).join(' │ ');
|
|
715
|
-
return `${i === 0 ? '📌 ' : ' '}${cols}`;
|
|
716
|
-
}).join('\n');
|
|
717
|
-
previewDiv.textContent = `Columns: ${df.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
718
|
-
}
|
|
719
|
-
catch (err) {
|
|
720
|
-
previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
712
|
+
]);
|
|
713
|
+
// Render modal to document.body and open it
|
|
714
|
+
this._reshapeModal.render(document.body);
|
|
715
|
+
this._reshapeModalRendered = true;
|
|
716
|
+
// Use requestAnimationFrame to ensure DOM is ready
|
|
717
|
+
requestAnimationFrame(() => {
|
|
718
|
+
const delimiterSelect = document.getElementById(`${this._id}-delimiter`);
|
|
719
|
+
const headerRowInput = document.getElementById(`${this._id}-header-row`);
|
|
720
|
+
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`);
|
|
721
|
+
const previewDiv = document.getElementById(`${this._id}-preview`);
|
|
722
|
+
// Auto-detect initial values
|
|
723
|
+
if (this._rawFileData?.text) {
|
|
724
|
+
const detected = this._driver._detectDelimiter(this._rawFileData.text);
|
|
725
|
+
if (delimiterSelect)
|
|
726
|
+
delimiterSelect.value = detected === '\t' ? '\\t' : detected;
|
|
727
|
+
const headerRow = this._driver._detectHeaderRow(this._rawFileData.text, detected);
|
|
728
|
+
if (headerRowInput)
|
|
729
|
+
headerRowInput.value = String(headerRow);
|
|
721
730
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
731
|
+
// Update preview on changes
|
|
732
|
+
const updatePreview = async () => {
|
|
733
|
+
if (!this._rawFileData?.text)
|
|
734
|
+
return;
|
|
735
|
+
const delim = delimiterSelect?.value === '\\t' ? '\t' : (delimiterSelect?.value || ',');
|
|
736
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
737
|
+
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
738
|
+
try {
|
|
739
|
+
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
740
|
+
delimiter: delim,
|
|
741
|
+
headerRow,
|
|
742
|
+
skipRows,
|
|
743
|
+
hasHeader: true,
|
|
744
|
+
maxRows: 10
|
|
745
|
+
});
|
|
746
|
+
const preview = df.toRows().map((row, i) => {
|
|
747
|
+
const cols = Object.values(row).map(v => String(v).padEnd(20)).join(' │ ');
|
|
748
|
+
return `${i === 0 ? '📌 ' : ' '}${cols}`;
|
|
749
|
+
}).join('\n');
|
|
750
|
+
if (previewDiv)
|
|
751
|
+
previewDiv.textContent = `Columns: ${df.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
752
|
+
}
|
|
753
|
+
catch (err) {
|
|
754
|
+
if (previewDiv)
|
|
755
|
+
previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
if (delimiterSelect)
|
|
759
|
+
delimiterSelect.addEventListener('change', updatePreview);
|
|
760
|
+
if (headerRowInput)
|
|
761
|
+
headerRowInput.addEventListener('input', updatePreview);
|
|
762
|
+
if (skipRowsInput)
|
|
763
|
+
skipRowsInput.addEventListener('input', updatePreview);
|
|
764
|
+
updatePreview();
|
|
765
|
+
this._reshapeModal.open();
|
|
766
|
+
});
|
|
728
767
|
}
|
|
729
768
|
update(_prop, _value) { }
|
|
730
769
|
/* ═══════════════════════════════════════════════════
|
|
@@ -63,6 +63,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
63
63
|
private _showReshapeWarning: boolean = true;
|
|
64
64
|
private _rawFileData: { file: File; text?: string; isExcel?: boolean } | null = null;
|
|
65
65
|
private _reshapeModal: Modal | null = null; // ✅ ADD THIS LINE
|
|
66
|
+
private _reshapeModalRendered: boolean = false; // Track if modal already rendered
|
|
66
67
|
|
|
67
68
|
constructor(id: string, options: DataFrameOptions = {}) {
|
|
68
69
|
super(id, {
|
|
@@ -503,25 +504,27 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
503
504
|
}
|
|
504
505
|
|
|
505
506
|
// ✅ Detect malformed data
|
|
506
|
-
const isMalformed = this._detectMalformedData(
|
|
507
|
+
const isMalformed = this._detectMalformedData(this._df!);
|
|
507
508
|
|
|
508
509
|
// ✅ Show warning if malformed
|
|
509
510
|
if (isMalformed && this._showReshapeWarning && this._rawFileData) {
|
|
510
511
|
this._updateStatus(
|
|
511
|
-
`⚠️ ${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may be malformed)`,
|
|
512
|
+
`⚠️ ${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols (Data may be malformed — headers may be on wrong row)`,
|
|
512
513
|
'warning'
|
|
513
514
|
);
|
|
514
515
|
|
|
515
|
-
// Add Settings button
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
516
|
+
// Add Fix Import Settings button after a tick to ensure status DOM is ready
|
|
517
|
+
requestAnimationFrame(() => {
|
|
518
|
+
const statusEl = document.getElementById(`${this._id}-status`);
|
|
519
|
+
if (statusEl) {
|
|
520
|
+
const settingsBtn = document.createElement('button');
|
|
521
|
+
settingsBtn.textContent = '⚙️ Fix Import Settings';
|
|
522
|
+
settingsBtn.className = 'jux-button jux-button-sm jux-button-warning';
|
|
523
|
+
settingsBtn.style.marginLeft = '0.5rem';
|
|
524
|
+
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
525
|
+
statusEl.appendChild(settingsBtn);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
525
528
|
} else {
|
|
526
529
|
this._updateStatus(
|
|
527
530
|
`${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols`,
|
|
@@ -530,15 +533,17 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
530
533
|
|
|
531
534
|
// ✅ Still add Settings button for manual adjustment
|
|
532
535
|
if (this._showReshapeWarning && this._rawFileData) {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
536
|
+
requestAnimationFrame(() => {
|
|
537
|
+
const statusEl = document.getElementById(`${this._id}-status`);
|
|
538
|
+
if (statusEl) {
|
|
539
|
+
const settingsBtn = document.createElement('button');
|
|
540
|
+
settingsBtn.textContent = '⚙️ Settings';
|
|
541
|
+
settingsBtn.className = 'jux-button jux-button-sm jux-button-ghost';
|
|
542
|
+
settingsBtn.style.marginLeft = '0.5rem';
|
|
543
|
+
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
544
|
+
statusEl.appendChild(settingsBtn);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
542
547
|
}
|
|
543
548
|
}
|
|
544
549
|
|
|
@@ -618,16 +623,22 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
618
623
|
private async _showExcelReshapeModal(): Promise<void> {
|
|
619
624
|
if (!this._rawFileData?.file) return;
|
|
620
625
|
|
|
621
|
-
//
|
|
622
|
-
if (
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
backdropClose: false
|
|
628
|
-
});
|
|
626
|
+
// Remove old modal from DOM if it was previously rendered
|
|
627
|
+
if (this._reshapeModal && this._reshapeModalRendered) {
|
|
628
|
+
const oldEl = document.getElementById(`${this._id}-reshape-modal`);
|
|
629
|
+
if (oldEl) oldEl.remove();
|
|
630
|
+
this._reshapeModal = null;
|
|
631
|
+
this._reshapeModalRendered = false;
|
|
629
632
|
}
|
|
630
633
|
|
|
634
|
+
// Create fresh modal
|
|
635
|
+
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
636
|
+
title: 'Excel Import Settings',
|
|
637
|
+
size: 'large',
|
|
638
|
+
close: true,
|
|
639
|
+
backdropClose: false
|
|
640
|
+
});
|
|
641
|
+
|
|
631
642
|
// Build modal content
|
|
632
643
|
const modalContent = document.createElement('div');
|
|
633
644
|
modalContent.innerHTML = `
|
|
@@ -688,6 +699,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
688
699
|
if (sheetNames.length > 1) {
|
|
689
700
|
this._renderMultiSheet(sheets, this._rawFileData!.file.name);
|
|
690
701
|
} else {
|
|
702
|
+
this._showReshapeWarning = false; // Prevent recursive warning after manual fix
|
|
691
703
|
this._setDataFrame(sheets[sheetNames[0]], this._rawFileData!.file.name);
|
|
692
704
|
}
|
|
693
705
|
|
|
@@ -697,15 +709,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
697
709
|
}
|
|
698
710
|
}
|
|
699
711
|
}
|
|
700
|
-
])
|
|
701
|
-
|
|
712
|
+
]);
|
|
713
|
+
|
|
714
|
+
// Render modal to document.body and open it
|
|
715
|
+
this._reshapeModal.render(document.body);
|
|
716
|
+
this._reshapeModalRendered = true;
|
|
717
|
+
|
|
718
|
+
// Wait a tick for DOM to update after render
|
|
719
|
+
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
702
720
|
|
|
703
721
|
const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
|
|
704
722
|
const previewDiv = document.getElementById(`${this._id}-preview`)!;
|
|
705
723
|
|
|
706
724
|
// Update preview on header row change
|
|
707
725
|
const updatePreview = async () => {
|
|
708
|
-
const headerRow = parseInt(headerRowInput
|
|
726
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
709
727
|
|
|
710
728
|
try {
|
|
711
729
|
const sheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
|
|
@@ -715,7 +733,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
715
733
|
|
|
716
734
|
const firstSheet = Object.values(sheets)[0];
|
|
717
735
|
if (!firstSheet) {
|
|
718
|
-
previewDiv.textContent = '⚠️ No data found';
|
|
736
|
+
if (previewDiv) previewDiv.textContent = '⚠️ No data found';
|
|
719
737
|
return;
|
|
720
738
|
}
|
|
721
739
|
|
|
@@ -724,13 +742,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
724
742
|
return `${i === 0 ? '📌 ' : ' '}${cols}`;
|
|
725
743
|
}).join('\n');
|
|
726
744
|
|
|
727
|
-
previewDiv.textContent = `Columns: ${firstSheet.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
745
|
+
if (previewDiv) previewDiv.textContent = `Columns: ${firstSheet.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
728
746
|
} catch (err: any) {
|
|
729
|
-
previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
747
|
+
if (previewDiv) previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
730
748
|
}
|
|
731
749
|
};
|
|
732
750
|
|
|
733
|
-
headerRowInput.addEventListener('input', updatePreview);
|
|
751
|
+
if (headerRowInput) headerRowInput.addEventListener('input', updatePreview);
|
|
734
752
|
updatePreview();
|
|
735
753
|
|
|
736
754
|
this._reshapeModal.open();
|
|
@@ -742,16 +760,22 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
742
760
|
private _showCSVReshapeModal(): void {
|
|
743
761
|
if (!this._rawFileData) return;
|
|
744
762
|
|
|
745
|
-
//
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
backdropClose: false
|
|
752
|
-
});
|
|
763
|
+
// Remove old modal from DOM if it was previously rendered
|
|
764
|
+
if (this._reshapeModal && this._reshapeModalRendered) {
|
|
765
|
+
const oldEl = document.getElementById(`${this._id}-reshape-modal`);
|
|
766
|
+
if (oldEl) oldEl.remove();
|
|
767
|
+
this._reshapeModal = null;
|
|
768
|
+
this._reshapeModalRendered = false;
|
|
753
769
|
}
|
|
754
770
|
|
|
771
|
+
// Create fresh modal
|
|
772
|
+
this._reshapeModal = new Modal(`${this._id}-reshape-modal`, {
|
|
773
|
+
title: 'CSV Import Settings',
|
|
774
|
+
size: 'large',
|
|
775
|
+
close: true,
|
|
776
|
+
backdropClose: false
|
|
777
|
+
});
|
|
778
|
+
|
|
755
779
|
// Build modal content
|
|
756
780
|
const modalContent = document.createElement('div');
|
|
757
781
|
modalContent.innerHTML = `
|
|
@@ -815,6 +839,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
815
839
|
});
|
|
816
840
|
|
|
817
841
|
await this._driver.store(this._rawFileData.file.name, df, { source: this._rawFileData.file.name });
|
|
842
|
+
this._showReshapeWarning = false; // Prevent recursive warning after manual fix
|
|
818
843
|
this._setDataFrame(df, this._rawFileData.file.name);
|
|
819
844
|
|
|
820
845
|
this._reshapeModal!.closeModal();
|
|
@@ -823,58 +848,64 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
823
848
|
}
|
|
824
849
|
}
|
|
825
850
|
}
|
|
826
|
-
])
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const
|
|
837
|
-
|
|
851
|
+
]);
|
|
852
|
+
|
|
853
|
+
// Render modal to document.body and open it
|
|
854
|
+
this._reshapeModal.render(document.body);
|
|
855
|
+
this._reshapeModalRendered = true;
|
|
856
|
+
|
|
857
|
+
// Use requestAnimationFrame to ensure DOM is ready
|
|
858
|
+
requestAnimationFrame(() => {
|
|
859
|
+
const delimiterSelect = document.getElementById(`${this._id}-delimiter`) as HTMLSelectElement;
|
|
860
|
+
const headerRowInput = document.getElementById(`${this._id}-header-row`) as HTMLInputElement;
|
|
861
|
+
const skipRowsInput = document.getElementById(`${this._id}-skip-rows`) as HTMLInputElement;
|
|
862
|
+
const previewDiv = document.getElementById(`${this._id}-preview`)!;
|
|
863
|
+
|
|
864
|
+
// Auto-detect initial values
|
|
865
|
+
if (this._rawFileData?.text) {
|
|
866
|
+
const detected = (this._driver as any)._detectDelimiter(this._rawFileData.text);
|
|
867
|
+
if (delimiterSelect) delimiterSelect.value = detected === '\t' ? '\\t' : detected;
|
|
868
|
+
|
|
869
|
+
const headerRow = (this._driver as any)._detectHeaderRow(this._rawFileData.text, detected);
|
|
870
|
+
if (headerRowInput) headerRowInput.value = String(headerRow);
|
|
871
|
+
}
|
|
838
872
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
873
|
+
// Update preview on changes
|
|
874
|
+
const updatePreview = async () => {
|
|
875
|
+
if (!this._rawFileData?.text) return;
|
|
842
876
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
877
|
+
const delim = delimiterSelect?.value === '\\t' ? '\t' : (delimiterSelect?.value || ',');
|
|
878
|
+
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
879
|
+
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
846
880
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
881
|
+
try {
|
|
882
|
+
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
883
|
+
delimiter: delim,
|
|
884
|
+
headerRow,
|
|
885
|
+
skipRows,
|
|
886
|
+
hasHeader: true,
|
|
887
|
+
maxRows: 10
|
|
888
|
+
});
|
|
850
889
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
skipRows,
|
|
856
|
-
hasHeader: true,
|
|
857
|
-
maxRows: 10
|
|
858
|
-
});
|
|
890
|
+
const preview = df.toRows().map((row, i) => {
|
|
891
|
+
const cols = Object.values(row).map(v => String(v).padEnd(20)).join(' │ ');
|
|
892
|
+
return `${i === 0 ? '📌 ' : ' '}${cols}`;
|
|
893
|
+
}).join('\n');
|
|
859
894
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
previewDiv.textContent = `Columns: ${df.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
866
|
-
} catch (err: any) {
|
|
867
|
-
previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
868
|
-
}
|
|
869
|
-
};
|
|
895
|
+
if (previewDiv) previewDiv.textContent = `Columns: ${df.columns.join(' │ ')}\n${'─'.repeat(80)}\n${preview}`;
|
|
896
|
+
} catch (err: any) {
|
|
897
|
+
if (previewDiv) previewDiv.textContent = `⚠️ Error: ${err.message}`;
|
|
898
|
+
}
|
|
899
|
+
};
|
|
870
900
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
901
|
+
if (delimiterSelect) delimiterSelect.addEventListener('change', updatePreview);
|
|
902
|
+
if (headerRowInput) headerRowInput.addEventListener('input', updatePreview);
|
|
903
|
+
if (skipRowsInput) skipRowsInput.addEventListener('input', updatePreview);
|
|
874
904
|
|
|
875
|
-
|
|
905
|
+
updatePreview();
|
|
876
906
|
|
|
877
|
-
|
|
907
|
+
this._reshapeModal!.open();
|
|
908
|
+
});
|
|
878
909
|
}
|
|
879
910
|
|
|
880
911
|
update(_prop: string, _value: any): void { }
|