juxscript 1.1.224 → 1.1.226
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 +16 -2
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +255 -76
- package/lib/components/dataframe.ts +281 -86
- package/lib/styles/shadcn.css +122 -0
- package/package.json +1 -1
|
@@ -64,6 +64,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
64
64
|
private _collapsed;
|
|
65
65
|
private _summaryTemplate;
|
|
66
66
|
private _detailsElement;
|
|
67
|
+
private _settingsModal;
|
|
67
68
|
constructor(id: string, options?: DataFrameOptions);
|
|
68
69
|
protected getTriggerEvents(): readonly string[];
|
|
69
70
|
protected getCallbackEvents(): readonly string[];
|
|
@@ -172,13 +173,26 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
172
173
|
private _updateStatus;
|
|
173
174
|
private _setDataFrame;
|
|
174
175
|
/**
|
|
175
|
-
*
|
|
176
|
+
* Show the data view (table + settings gear), hide upload area
|
|
176
177
|
*/
|
|
177
|
-
private
|
|
178
|
+
private _showDataView;
|
|
179
|
+
/**
|
|
180
|
+
* Hide data view, show upload area
|
|
181
|
+
*/
|
|
182
|
+
private _hideDataView;
|
|
183
|
+
/**
|
|
184
|
+
* Update the settings gear tooltip/info
|
|
185
|
+
*/
|
|
186
|
+
private _updateSettingsGear;
|
|
187
|
+
/**
|
|
188
|
+
* Show the unified settings modal
|
|
189
|
+
*/
|
|
190
|
+
private _showSettingsModal;
|
|
178
191
|
/**
|
|
179
192
|
* Update the collapsible summary text
|
|
180
193
|
*/
|
|
181
194
|
private _updateSummary;
|
|
195
|
+
private _appendSettingsButton;
|
|
182
196
|
private _detectMalformedData;
|
|
183
197
|
private _showReshapeModal;
|
|
184
198
|
private _cleanupReshapeModal;
|
|
@@ -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;IACf,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAEnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,MAAM,CAAC;CAC/C;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;IAC/C,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,yBAAyB,CAAiB;IAClD,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,oBAAoB,CAAqB;IACjD,OAAO,CAAC,aAAa,CAAuC;IAC5D,OAAO,CAAC,kBAAkB,CAAc;IACxC,OAAO,CAAC,eAAe,CAAiB;IAExC,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,gBAAgB,CAA4C;IACpE,OAAO,CAAC,eAAe,CAAmC;
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,6 +52,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
52
52
|
this._collapsed = false;
|
|
53
53
|
this._summaryTemplate = null;
|
|
54
54
|
this._detailsElement = null;
|
|
55
|
+
this._settingsModal = null;
|
|
55
56
|
this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
|
|
56
57
|
this._showStatus = options.showStatus ?? true;
|
|
57
58
|
this._icon = options.icon ?? '';
|
|
@@ -253,17 +254,15 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
253
254
|
if (existingTabs)
|
|
254
255
|
existingTabs.remove();
|
|
255
256
|
}
|
|
256
|
-
// ✅ Hide
|
|
257
|
+
// ✅ Hide data view, show upload
|
|
258
|
+
this._hideDataView();
|
|
259
|
+
// ✅ Hide collapsible details
|
|
257
260
|
if (this._collapsible && this._detailsElement) {
|
|
258
261
|
this._detailsElement.style.display = 'none';
|
|
259
262
|
}
|
|
260
|
-
// ✅
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
statusEl.style.display = 'none';
|
|
264
|
-
}
|
|
265
|
-
else if (statusEl) {
|
|
266
|
-
this._updateStatus('No data loaded.', 'empty');
|
|
263
|
+
// ✅ Clear file from upload component
|
|
264
|
+
if (this._uploadRef) {
|
|
265
|
+
this._uploadRef.clear();
|
|
267
266
|
}
|
|
268
267
|
return this;
|
|
269
268
|
}
|
|
@@ -543,23 +542,28 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
543
542
|
const el = document.getElementById(`${this._id}-status`);
|
|
544
543
|
if (!el)
|
|
545
544
|
return;
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
545
|
+
// ✅ Only show status during loading/error states
|
|
546
|
+
if (type === 'loading' || type === 'error') {
|
|
547
|
+
el.style.display = '';
|
|
548
|
+
el.className = 'jux-dataframe-status';
|
|
550
549
|
el.classList.add(`jux-dataframe-status-${type}`);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
550
|
+
el.innerHTML = '';
|
|
551
|
+
if (this._icon && type === 'error') {
|
|
552
|
+
const iconEl = renderIcon(this._icon);
|
|
553
|
+
iconEl.style.width = '16px';
|
|
554
|
+
iconEl.style.height = '16px';
|
|
555
|
+
iconEl.style.marginRight = '6px';
|
|
556
|
+
iconEl.style.verticalAlign = 'middle';
|
|
557
|
+
el.appendChild(iconEl);
|
|
558
|
+
}
|
|
559
|
+
const span = document.createElement('span');
|
|
560
|
+
span.textContent = text;
|
|
561
|
+
el.appendChild(span);
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
// ✅ Hide status after successful load - table speaks for itself
|
|
565
|
+
el.style.display = 'none';
|
|
559
566
|
}
|
|
560
|
-
const span = document.createElement('span');
|
|
561
|
-
span.textContent = text;
|
|
562
|
-
el.appendChild(span);
|
|
563
567
|
}
|
|
564
568
|
_setDataFrame(df, sourceName) {
|
|
565
569
|
this._df = df;
|
|
@@ -589,66 +593,226 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
589
593
|
const columnDefs = this._df.columns.map(col => ({ key: col, label: col }));
|
|
590
594
|
this._table.columns(columnDefs).rows(this._df.toRows());
|
|
591
595
|
}
|
|
592
|
-
// ✅ Show the
|
|
596
|
+
// ✅ Show the table container, hide upload area
|
|
597
|
+
this._showDataView();
|
|
598
|
+
// ✅ Update collapsible summary if enabled
|
|
593
599
|
if (this._collapsible && this._detailsElement) {
|
|
594
600
|
this._detailsElement.style.display = '';
|
|
595
601
|
this._updateSummary();
|
|
596
602
|
}
|
|
603
|
+
// ✅ Hide status - data is loaded
|
|
604
|
+
this._updateStatus('', 'success');
|
|
605
|
+
this._triggerCallback('load', this._df, null, this);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Show the data view (table + settings gear), hide upload area
|
|
609
|
+
*/
|
|
610
|
+
_showDataView() {
|
|
611
|
+
const wrapper = document.getElementById(this._id);
|
|
612
|
+
if (!wrapper)
|
|
613
|
+
return;
|
|
614
|
+
// Hide upload area
|
|
615
|
+
const uploadArea = wrapper.querySelector('.jux-dataframe-upload-area');
|
|
616
|
+
if (uploadArea) {
|
|
617
|
+
uploadArea.style.display = 'none';
|
|
618
|
+
}
|
|
619
|
+
// Show data container (table + toolbar)
|
|
620
|
+
const dataContainer = wrapper.querySelector('.jux-dataframe-data');
|
|
621
|
+
if (dataContainer) {
|
|
622
|
+
dataContainer.style.display = '';
|
|
623
|
+
}
|
|
624
|
+
// Update settings gear with file info
|
|
625
|
+
this._updateSettingsGear();
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Hide data view, show upload area
|
|
629
|
+
*/
|
|
630
|
+
_hideDataView() {
|
|
631
|
+
const wrapper = document.getElementById(this._id);
|
|
632
|
+
if (!wrapper)
|
|
633
|
+
return;
|
|
634
|
+
// Show upload area
|
|
635
|
+
const uploadArea = wrapper.querySelector('.jux-dataframe-upload-area');
|
|
636
|
+
if (uploadArea) {
|
|
637
|
+
uploadArea.style.display = '';
|
|
638
|
+
}
|
|
639
|
+
// Hide data container
|
|
640
|
+
const dataContainer = wrapper.querySelector('.jux-dataframe-data');
|
|
641
|
+
if (dataContainer) {
|
|
642
|
+
dataContainer.style.display = 'none';
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Update the settings gear tooltip/info
|
|
647
|
+
*/
|
|
648
|
+
_updateSettingsGear() {
|
|
649
|
+
const gear = document.getElementById(`${this._id}-settings-gear`);
|
|
650
|
+
if (!gear || !this._df)
|
|
651
|
+
return;
|
|
597
652
|
const isMalformed = this._detectMalformedData(this._df);
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
this._appendSettingsButton('Fix Import Settings', 'warning');
|
|
653
|
+
if (isMalformed) {
|
|
654
|
+
gear.classList.add('jux-dataframe-gear-warning');
|
|
655
|
+
gear.title = `${this.state.sourceName} — May need reformatting`;
|
|
602
656
|
}
|
|
603
657
|
else {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
this._appendSettingsButton('Import Settings', 'ghost');
|
|
607
|
-
}
|
|
658
|
+
gear.classList.remove('jux-dataframe-gear-warning');
|
|
659
|
+
gear.title = `${this.state.sourceName} — ${this._df.height} rows × ${this._df.width} cols`;
|
|
608
660
|
}
|
|
609
|
-
this._triggerCallback('load', this._df, null, this);
|
|
610
661
|
}
|
|
611
662
|
/**
|
|
612
|
-
*
|
|
663
|
+
* Show the unified settings modal
|
|
613
664
|
*/
|
|
614
|
-
|
|
665
|
+
_showSettingsModal() {
|
|
666
|
+
this._cleanupReshapeModal();
|
|
667
|
+
const fileInfo = this._rawFileData?.file;
|
|
668
|
+
const isMalformed = this._df ? this._detectMalformedData(this._df) : false;
|
|
669
|
+
this._settingsModal = new Modal(`${this._id}-settings-modal`, {
|
|
670
|
+
title: 'Data Settings',
|
|
671
|
+
size: 'medium',
|
|
672
|
+
close: true,
|
|
673
|
+
backdropClose: true
|
|
674
|
+
});
|
|
675
|
+
const fileSizeKB = fileInfo ? (fileInfo.size / 1024).toFixed(1) : '0';
|
|
676
|
+
const fileName = fileInfo?.name || this.state.sourceName || 'Unknown';
|
|
677
|
+
let contentHTML = `
|
|
678
|
+
<div class="jux-dataframe-settings-content">
|
|
679
|
+
<!-- File Info Section -->
|
|
680
|
+
<div class="jux-dataframe-settings-section">
|
|
681
|
+
<div class="jux-dataframe-settings-label">Source</div>
|
|
682
|
+
<div class="jux-dataframe-settings-value">
|
|
683
|
+
<strong>${this._escapeHtml(fileName)}</strong>
|
|
684
|
+
${fileInfo ? `<span class="jux-muted" style="margin-left: 8px;">${fileSizeKB} KB</span>` : ''}
|
|
685
|
+
</div>
|
|
686
|
+
</div>
|
|
687
|
+
|
|
688
|
+
<!-- Data Info Section -->
|
|
689
|
+
<div class="jux-dataframe-settings-section">
|
|
690
|
+
<div class="jux-dataframe-settings-label">Data</div>
|
|
691
|
+
<div class="jux-dataframe-settings-value">
|
|
692
|
+
${this._df ? `${this._df.height} rows × ${this._df.width} columns` : 'No data loaded'}
|
|
693
|
+
${isMalformed ? '<span style="color: hsl(var(--warning)); margin-left: 8px;">⚠️ May need reformatting</span>' : ''}
|
|
694
|
+
</div>
|
|
695
|
+
</div>
|
|
696
|
+
`;
|
|
697
|
+
// Import Settings button (only if we have raw file data)
|
|
698
|
+
if (this._rawFileData) {
|
|
699
|
+
contentHTML += `
|
|
700
|
+
<div class="jux-dataframe-settings-section">
|
|
701
|
+
<div class="jux-dataframe-settings-label">Import</div>
|
|
702
|
+
<div class="jux-dataframe-settings-value">
|
|
703
|
+
<button id="${this._id}-adjust-import" class="jux-button jux-button-outline jux-button-sm">
|
|
704
|
+
⚙️ Adjust Header Row / Delimiter
|
|
705
|
+
</button>
|
|
706
|
+
</div>
|
|
707
|
+
</div>
|
|
708
|
+
`;
|
|
709
|
+
}
|
|
710
|
+
contentHTML += `
|
|
711
|
+
</div>
|
|
712
|
+
`;
|
|
713
|
+
this._settingsModal
|
|
714
|
+
.content(contentHTML)
|
|
715
|
+
.actions([
|
|
716
|
+
{
|
|
717
|
+
label: 'Remove Data',
|
|
718
|
+
variant: 'secondary',
|
|
719
|
+
click: async () => {
|
|
720
|
+
await this.clear();
|
|
721
|
+
this._settingsModal.closeModal();
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
label: 'Done',
|
|
726
|
+
variant: 'primary',
|
|
727
|
+
click: () => this._settingsModal.closeModal()
|
|
728
|
+
}
|
|
729
|
+
]);
|
|
730
|
+
this._settingsModal.render(document.body);
|
|
731
|
+
this._settingsModal.open();
|
|
732
|
+
// Wire up import settings button
|
|
615
733
|
requestAnimationFrame(() => {
|
|
616
|
-
const
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
623
|
-
statusEl.appendChild(settingsBtn);
|
|
734
|
+
const adjustBtn = document.getElementById(`${this._id}-adjust-import`);
|
|
735
|
+
if (adjustBtn) {
|
|
736
|
+
adjustBtn.addEventListener('click', () => {
|
|
737
|
+
this._settingsModal.closeModal();
|
|
738
|
+
this._showReshapeModal();
|
|
739
|
+
});
|
|
624
740
|
}
|
|
625
741
|
});
|
|
626
742
|
}
|
|
627
743
|
/**
|
|
628
744
|
* Update the collapsible summary text
|
|
629
745
|
*/
|
|
630
|
-
_updateSummary() {
|
|
746
|
+
_updateSummary(isMalformed) {
|
|
631
747
|
if (!this._collapsible || !this._detailsElement)
|
|
632
748
|
return;
|
|
633
|
-
const
|
|
634
|
-
if (!
|
|
635
|
-
return;
|
|
636
|
-
if (!this._df) {
|
|
637
|
-
// Don't show anything if no data
|
|
749
|
+
const summaryEl = this._detailsElement.querySelector('.jux-dataframe-summary');
|
|
750
|
+
if (!summaryEl)
|
|
638
751
|
return;
|
|
752
|
+
const malformed = isMalformed ?? (this._df ? this._detectMalformedData(this._df) : false);
|
|
753
|
+
const summaryTextEl = summaryEl.querySelector('.jux-dataframe-summary-text');
|
|
754
|
+
if (summaryTextEl) {
|
|
755
|
+
if (!this._df) {
|
|
756
|
+
summaryTextEl.textContent = 'No data loaded';
|
|
757
|
+
}
|
|
758
|
+
else if (this._summaryTemplate) {
|
|
759
|
+
summaryTextEl.textContent = this._summaryTemplate(this._df);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
const suffix = malformed ? ' ⚠️' : '';
|
|
763
|
+
summaryTextEl.textContent = `${this.state.sourceName || 'Data'} — ${this._df.height} rows × ${this._df.width} cols${suffix}`;
|
|
764
|
+
}
|
|
639
765
|
}
|
|
640
|
-
|
|
641
|
-
|
|
766
|
+
// ✅ Add/update settings gear in summary
|
|
767
|
+
let gearBtn = summaryEl.querySelector('.jux-dataframe-summary-gear');
|
|
768
|
+
if (this._df) {
|
|
769
|
+
if (!gearBtn) {
|
|
770
|
+
gearBtn = document.createElement('button');
|
|
771
|
+
gearBtn.className = 'jux-dataframe-summary-gear';
|
|
772
|
+
gearBtn.type = 'button';
|
|
773
|
+
gearBtn.innerHTML = '⚙️';
|
|
774
|
+
gearBtn.addEventListener('click', (e) => {
|
|
775
|
+
e.stopPropagation();
|
|
776
|
+
this._showSettingsModal();
|
|
777
|
+
});
|
|
778
|
+
summaryEl.appendChild(gearBtn);
|
|
779
|
+
}
|
|
780
|
+
if (malformed) {
|
|
781
|
+
gearBtn.classList.add('jux-dataframe-gear-warning');
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
gearBtn.classList.remove('jux-dataframe-gear-warning');
|
|
785
|
+
}
|
|
642
786
|
}
|
|
643
|
-
else {
|
|
644
|
-
|
|
645
|
-
const suffix = isMalformed ? ' ⚠️' : '';
|
|
646
|
-
summaryTextEl.textContent = `${this.state.sourceName || 'Data'} — ${this._df.height} rows × ${this._df.width} cols${suffix}`;
|
|
787
|
+
else if (gearBtn) {
|
|
788
|
+
gearBtn.remove();
|
|
647
789
|
}
|
|
648
790
|
}
|
|
649
791
|
/* ═══════════════════════════════════════════════════
|
|
650
792
|
* MALFORMED DATA DETECTION
|
|
651
793
|
* ═══════════════════════════════════════════════════ */
|
|
794
|
+
_appendSettingsButton(label, variant) {
|
|
795
|
+
const statusEl = document.getElementById(`${this._id}-status`);
|
|
796
|
+
if (!statusEl)
|
|
797
|
+
return;
|
|
798
|
+
// Remove existing settings button if any
|
|
799
|
+
const existing = statusEl.querySelector('.jux-dataframe-settings-btn');
|
|
800
|
+
if (existing)
|
|
801
|
+
existing.remove();
|
|
802
|
+
const btn = document.createElement('button');
|
|
803
|
+
btn.className = `jux-dataframe-settings-btn jux-dataframe-settings-btn-${variant}`;
|
|
804
|
+
btn.type = 'button';
|
|
805
|
+
btn.textContent = `⚙️ ${label}`;
|
|
806
|
+
btn.style.marginLeft = '8px';
|
|
807
|
+
btn.style.cursor = 'pointer';
|
|
808
|
+
btn.style.fontSize = '0.8rem';
|
|
809
|
+
btn.style.padding = '2px 8px';
|
|
810
|
+
btn.style.borderRadius = 'var(--radius, 4px)';
|
|
811
|
+
btn.style.border = '1px solid hsl(var(--border))';
|
|
812
|
+
btn.style.background = variant === 'warning' ? 'hsl(38 92% 50% / 0.15)' : 'transparent';
|
|
813
|
+
btn.addEventListener('click', () => this._showReshapeModal());
|
|
814
|
+
statusEl.appendChild(btn);
|
|
815
|
+
}
|
|
652
816
|
_detectMalformedData(df) {
|
|
653
817
|
const columns = df.columns;
|
|
654
818
|
const rows = df.toRows();
|
|
@@ -771,8 +935,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
771
935
|
if (nonEmpty.length < values.length * 0.5)
|
|
772
936
|
continue;
|
|
773
937
|
const nonNumeric = nonEmpty.filter(v => {
|
|
774
|
-
const
|
|
775
|
-
return isNaN(Number(
|
|
938
|
+
const trimmed = v.trim();
|
|
939
|
+
return isNaN(Number(trimmed)) && trimmed !== '';
|
|
776
940
|
}).length;
|
|
777
941
|
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
778
942
|
selectedSheetRow = sheetRow;
|
|
@@ -1045,8 +1209,10 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1045
1209
|
wrapper.className += ` ${className}`;
|
|
1046
1210
|
if (style)
|
|
1047
1211
|
wrapper.setAttribute('style', style);
|
|
1212
|
+
// ═══════════════════════════════════════════════════
|
|
1213
|
+
// UPLOAD AREA (shown initially, hidden after data loads)
|
|
1214
|
+
// ═══════════════════════════════════════════════════
|
|
1048
1215
|
if (this._inlineUpload) {
|
|
1049
|
-
// ✅ NEW: Enhanced upload area with better styling
|
|
1050
1216
|
const uploadArea = document.createElement('div');
|
|
1051
1217
|
uploadArea.className = 'jux-dataframe-upload-area';
|
|
1052
1218
|
uploadArea.id = `${this._id}-upload-area`;
|
|
@@ -1060,11 +1226,9 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1060
1226
|
}
|
|
1061
1227
|
const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
|
|
1062
1228
|
this._uploadRef = upload;
|
|
1063
|
-
// ✅ Handle file change AND file clear
|
|
1064
1229
|
this._pendingSource = async () => {
|
|
1065
1230
|
upload.bind('change', async (files) => {
|
|
1066
1231
|
if (!files || files.length === 0) {
|
|
1067
|
-
// File was cleared - reset the table
|
|
1068
1232
|
await this.clear();
|
|
1069
1233
|
return;
|
|
1070
1234
|
}
|
|
@@ -1075,12 +1239,12 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1075
1239
|
uploadContainer.className = 'jux-dataframe-upload';
|
|
1076
1240
|
uploadContainer.id = `${this._id}-upload-container`;
|
|
1077
1241
|
uploadArea.appendChild(uploadContainer);
|
|
1078
|
-
//
|
|
1242
|
+
// Status bar (for loading/error states only)
|
|
1079
1243
|
if (this._showStatus) {
|
|
1080
1244
|
const statusBar = document.createElement('div');
|
|
1081
|
-
statusBar.className = 'jux-dataframe-status
|
|
1245
|
+
statusBar.className = 'jux-dataframe-status';
|
|
1082
1246
|
statusBar.id = `${this._id}-status`;
|
|
1083
|
-
statusBar.style.display = 'none';
|
|
1247
|
+
statusBar.style.display = 'none';
|
|
1084
1248
|
uploadArea.appendChild(statusBar);
|
|
1085
1249
|
}
|
|
1086
1250
|
if (this._uploadDescription) {
|
|
@@ -1095,22 +1259,34 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1095
1259
|
}
|
|
1096
1260
|
else {
|
|
1097
1261
|
container.appendChild(wrapper);
|
|
1098
|
-
// ✅ Status bar for non-upload mode
|
|
1099
|
-
if (this._showStatus) {
|
|
1100
|
-
const statusBar = document.createElement('div');
|
|
1101
|
-
statusBar.className = 'jux-dataframe-status jux-dataframe-status-empty';
|
|
1102
|
-
statusBar.id = `${this._id}-status`;
|
|
1103
|
-
statusBar.textContent = 'No data loaded.';
|
|
1104
|
-
wrapper.appendChild(statusBar);
|
|
1105
|
-
}
|
|
1106
1262
|
}
|
|
1107
|
-
//
|
|
1263
|
+
// ═══════════════════════════════════════════════════
|
|
1264
|
+
// DATA CONTAINER (hidden initially, shown after data loads)
|
|
1265
|
+
// ═══════════════════════════════════════════════════
|
|
1266
|
+
const dataContainer = document.createElement('div');
|
|
1267
|
+
dataContainer.className = 'jux-dataframe-data';
|
|
1268
|
+
dataContainer.style.display = 'none'; // Hidden until data loads
|
|
1269
|
+
// ✅ Toolbar with settings gear
|
|
1270
|
+
const toolbar = document.createElement('div');
|
|
1271
|
+
toolbar.className = 'jux-dataframe-toolbar';
|
|
1272
|
+
const settingsGear = document.createElement('button');
|
|
1273
|
+
settingsGear.className = 'jux-dataframe-gear';
|
|
1274
|
+
settingsGear.id = `${this._id}-settings-gear`;
|
|
1275
|
+
settingsGear.type = 'button';
|
|
1276
|
+
settingsGear.innerHTML = '⚙️';
|
|
1277
|
+
settingsGear.title = 'Data Settings';
|
|
1278
|
+
settingsGear.addEventListener('click', () => this._showSettingsModal());
|
|
1279
|
+
toolbar.appendChild(settingsGear);
|
|
1280
|
+
dataContainer.appendChild(toolbar);
|
|
1281
|
+
// ═══════════════════════════════════════════════════
|
|
1282
|
+
// TABLE CONTAINER (inside data container or collapsible)
|
|
1283
|
+
// ═══════════════════════════════════════════════════
|
|
1108
1284
|
let tableContainer;
|
|
1109
1285
|
if (this._collapsible) {
|
|
1110
1286
|
const details = document.createElement('details');
|
|
1111
1287
|
details.className = 'jux-dataframe-details';
|
|
1112
1288
|
details.open = !this._collapsed;
|
|
1113
|
-
details.style.display = 'none'; //
|
|
1289
|
+
details.style.display = 'none'; // Hidden until data loads
|
|
1114
1290
|
this._detailsElement = details;
|
|
1115
1291
|
const summary = document.createElement('summary');
|
|
1116
1292
|
summary.className = 'jux-dataframe-summary';
|
|
@@ -1120,21 +1296,24 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1120
1296
|
summary.appendChild(chevron);
|
|
1121
1297
|
const textSpan = document.createElement('span');
|
|
1122
1298
|
textSpan.className = 'jux-dataframe-summary-text';
|
|
1123
|
-
// Don't set text - will be set when data loads
|
|
1124
1299
|
summary.appendChild(textSpan);
|
|
1125
1300
|
details.appendChild(summary);
|
|
1126
1301
|
const content = document.createElement('div');
|
|
1127
1302
|
content.className = 'jux-dataframe-details-content';
|
|
1128
1303
|
details.appendChild(content);
|
|
1129
|
-
|
|
1304
|
+
dataContainer.appendChild(details);
|
|
1130
1305
|
tableContainer = content;
|
|
1131
1306
|
details.addEventListener('toggle', () => {
|
|
1132
1307
|
this._collapsed = !details.open;
|
|
1133
1308
|
});
|
|
1134
1309
|
}
|
|
1135
1310
|
else {
|
|
1136
|
-
tableContainer =
|
|
1311
|
+
tableContainer = dataContainer;
|
|
1137
1312
|
}
|
|
1313
|
+
wrapper.appendChild(dataContainer);
|
|
1314
|
+
// ═══════════════════════════════════════════════════
|
|
1315
|
+
// TABLE
|
|
1316
|
+
// ═══════════════════════════════════════════════════
|
|
1138
1317
|
const tbl = new Table(`${this._id}-table`, {
|
|
1139
1318
|
striped: this._tableOptions.striped,
|
|
1140
1319
|
hoverable: this._tableOptions.hoverable,
|
|
@@ -83,6 +83,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
83
83
|
private _collapsed: boolean = false;
|
|
84
84
|
private _summaryTemplate: ((df: DataFrame) => string) | null = null;
|
|
85
85
|
private _detailsElement: HTMLDetailsElement | null = null;
|
|
86
|
+
private _settingsModal: Modal | null = null;
|
|
86
87
|
|
|
87
88
|
constructor(id: string, options: DataFrameOptions = {}) {
|
|
88
89
|
super(id, {
|
|
@@ -310,17 +311,17 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
310
311
|
if (existingTabs) existingTabs.remove();
|
|
311
312
|
}
|
|
312
313
|
|
|
313
|
-
// ✅ Hide
|
|
314
|
+
// ✅ Hide data view, show upload
|
|
315
|
+
this._hideDataView();
|
|
316
|
+
|
|
317
|
+
// ✅ Hide collapsible details
|
|
314
318
|
if (this._collapsible && this._detailsElement) {
|
|
315
319
|
this._detailsElement.style.display = 'none';
|
|
316
320
|
}
|
|
317
321
|
|
|
318
|
-
// ✅
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
statusEl.style.display = 'none';
|
|
322
|
-
} else if (statusEl) {
|
|
323
|
-
this._updateStatus('No data loaded.', 'empty');
|
|
322
|
+
// ✅ Clear file from upload component
|
|
323
|
+
if (this._uploadRef) {
|
|
324
|
+
this._uploadRef.clear();
|
|
324
325
|
}
|
|
325
326
|
|
|
326
327
|
return this;
|
|
@@ -658,25 +659,29 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
658
659
|
const el = document.getElementById(`${this._id}-status`);
|
|
659
660
|
if (!el) return;
|
|
660
661
|
|
|
661
|
-
//
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
662
|
+
// ✅ Only show status during loading/error states
|
|
663
|
+
if (type === 'loading' || type === 'error') {
|
|
664
|
+
el.style.display = '';
|
|
665
|
+
el.className = 'jux-dataframe-status';
|
|
666
|
+
el.classList.add(`jux-dataframe-status-${type}`);
|
|
667
|
+
el.innerHTML = '';
|
|
668
|
+
|
|
669
|
+
if (this._icon && type === 'error') {
|
|
670
|
+
const iconEl = renderIcon(this._icon);
|
|
671
|
+
iconEl.style.width = '16px';
|
|
672
|
+
iconEl.style.height = '16px';
|
|
673
|
+
iconEl.style.marginRight = '6px';
|
|
674
|
+
iconEl.style.verticalAlign = 'middle';
|
|
675
|
+
el.appendChild(iconEl);
|
|
676
|
+
}
|
|
667
677
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
el.appendChild(iconEl);
|
|
678
|
+
const span = document.createElement('span');
|
|
679
|
+
span.textContent = text;
|
|
680
|
+
el.appendChild(span);
|
|
681
|
+
} else {
|
|
682
|
+
// ✅ Hide status after successful load - table speaks for itself
|
|
683
|
+
el.style.display = 'none';
|
|
675
684
|
}
|
|
676
|
-
|
|
677
|
-
const span = document.createElement('span');
|
|
678
|
-
span.textContent = text;
|
|
679
|
-
el.appendChild(span);
|
|
680
685
|
}
|
|
681
686
|
|
|
682
687
|
private _setDataFrame(df: DataFrame, sourceName: string): void {
|
|
@@ -710,47 +715,169 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
710
715
|
this._table.columns(columnDefs).rows(this._df.toRows());
|
|
711
716
|
}
|
|
712
717
|
|
|
713
|
-
// ✅ Show the
|
|
718
|
+
// ✅ Show the table container, hide upload area
|
|
719
|
+
this._showDataView();
|
|
720
|
+
|
|
721
|
+
// ✅ Update collapsible summary if enabled
|
|
714
722
|
if (this._collapsible && this._detailsElement) {
|
|
715
723
|
this._detailsElement.style.display = '';
|
|
716
724
|
this._updateSummary();
|
|
717
725
|
}
|
|
718
726
|
|
|
719
|
-
|
|
727
|
+
// ✅ Hide status - data is loaded
|
|
728
|
+
this._updateStatus('', 'success');
|
|
720
729
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
}
|
|
730
|
+
this._triggerCallback('load', this._df, null, this);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Show the data view (table + settings gear), hide upload area
|
|
735
|
+
*/
|
|
736
|
+
private _showDataView(): void {
|
|
737
|
+
const wrapper = document.getElementById(this._id);
|
|
738
|
+
if (!wrapper) return;
|
|
739
|
+
|
|
740
|
+
// Hide upload area
|
|
741
|
+
const uploadArea = wrapper.querySelector('.jux-dataframe-upload-area') as HTMLElement;
|
|
742
|
+
if (uploadArea) {
|
|
743
|
+
uploadArea.style.display = 'none';
|
|
736
744
|
}
|
|
737
745
|
|
|
738
|
-
|
|
746
|
+
// Show data container (table + toolbar)
|
|
747
|
+
const dataContainer = wrapper.querySelector('.jux-dataframe-data') as HTMLElement;
|
|
748
|
+
if (dataContainer) {
|
|
749
|
+
dataContainer.style.display = '';
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Update settings gear with file info
|
|
753
|
+
this._updateSettingsGear();
|
|
739
754
|
}
|
|
740
755
|
|
|
741
756
|
/**
|
|
742
|
-
*
|
|
757
|
+
* Hide data view, show upload area
|
|
743
758
|
*/
|
|
744
|
-
private
|
|
759
|
+
private _hideDataView(): void {
|
|
760
|
+
const wrapper = document.getElementById(this._id);
|
|
761
|
+
if (!wrapper) return;
|
|
762
|
+
|
|
763
|
+
// Show upload area
|
|
764
|
+
const uploadArea = wrapper.querySelector('.jux-dataframe-upload-area') as HTMLElement;
|
|
765
|
+
if (uploadArea) {
|
|
766
|
+
uploadArea.style.display = '';
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Hide data container
|
|
770
|
+
const dataContainer = wrapper.querySelector('.jux-dataframe-data') as HTMLElement;
|
|
771
|
+
if (dataContainer) {
|
|
772
|
+
dataContainer.style.display = 'none';
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Update the settings gear tooltip/info
|
|
778
|
+
*/
|
|
779
|
+
private _updateSettingsGear(): void {
|
|
780
|
+
const gear = document.getElementById(`${this._id}-settings-gear`);
|
|
781
|
+
if (!gear || !this._df) return;
|
|
782
|
+
|
|
783
|
+
const isMalformed = this._detectMalformedData(this._df);
|
|
784
|
+
|
|
785
|
+
if (isMalformed) {
|
|
786
|
+
gear.classList.add('jux-dataframe-gear-warning');
|
|
787
|
+
gear.title = `${this.state.sourceName} — May need reformatting`;
|
|
788
|
+
} else {
|
|
789
|
+
gear.classList.remove('jux-dataframe-gear-warning');
|
|
790
|
+
gear.title = `${this.state.sourceName} — ${this._df.height} rows × ${this._df.width} cols`;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Show the unified settings modal
|
|
796
|
+
*/
|
|
797
|
+
private _showSettingsModal(): void {
|
|
798
|
+
this._cleanupReshapeModal();
|
|
799
|
+
|
|
800
|
+
const fileInfo = this._rawFileData?.file;
|
|
801
|
+
const isMalformed = this._df ? this._detectMalformedData(this._df) : false;
|
|
802
|
+
|
|
803
|
+
this._settingsModal = new Modal(`${this._id}-settings-modal`, {
|
|
804
|
+
title: 'Data Settings',
|
|
805
|
+
size: 'medium',
|
|
806
|
+
close: true,
|
|
807
|
+
backdropClose: true
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
const fileSizeKB = fileInfo ? (fileInfo.size / 1024).toFixed(1) : '0';
|
|
811
|
+
const fileName = fileInfo?.name || this.state.sourceName || 'Unknown';
|
|
812
|
+
|
|
813
|
+
let contentHTML = `
|
|
814
|
+
<div class="jux-dataframe-settings-content">
|
|
815
|
+
<!-- File Info Section -->
|
|
816
|
+
<div class="jux-dataframe-settings-section">
|
|
817
|
+
<div class="jux-dataframe-settings-label">Source</div>
|
|
818
|
+
<div class="jux-dataframe-settings-value">
|
|
819
|
+
<strong>${this._escapeHtml(fileName)}</strong>
|
|
820
|
+
${fileInfo ? `<span class="jux-muted" style="margin-left: 8px;">${fileSizeKB} KB</span>` : ''}
|
|
821
|
+
</div>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<!-- Data Info Section -->
|
|
825
|
+
<div class="jux-dataframe-settings-section">
|
|
826
|
+
<div class="jux-dataframe-settings-label">Data</div>
|
|
827
|
+
<div class="jux-dataframe-settings-value">
|
|
828
|
+
${this._df ? `${this._df.height} rows × ${this._df.width} columns` : 'No data loaded'}
|
|
829
|
+
${isMalformed ? '<span style="color: hsl(var(--warning)); margin-left: 8px;">⚠️ May need reformatting</span>' : ''}
|
|
830
|
+
</div>
|
|
831
|
+
</div>
|
|
832
|
+
`;
|
|
833
|
+
|
|
834
|
+
// Import Settings button (only if we have raw file data)
|
|
835
|
+
if (this._rawFileData) {
|
|
836
|
+
contentHTML += `
|
|
837
|
+
<div class="jux-dataframe-settings-section">
|
|
838
|
+
<div class="jux-dataframe-settings-label">Import</div>
|
|
839
|
+
<div class="jux-dataframe-settings-value">
|
|
840
|
+
<button id="${this._id}-adjust-import" class="jux-button jux-button-outline jux-button-sm">
|
|
841
|
+
⚙️ Adjust Header Row / Delimiter
|
|
842
|
+
</button>
|
|
843
|
+
</div>
|
|
844
|
+
</div>
|
|
845
|
+
`;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
contentHTML += `
|
|
849
|
+
</div>
|
|
850
|
+
`;
|
|
851
|
+
|
|
852
|
+
this._settingsModal
|
|
853
|
+
.content(contentHTML)
|
|
854
|
+
.actions([
|
|
855
|
+
{
|
|
856
|
+
label: 'Remove Data',
|
|
857
|
+
variant: 'secondary',
|
|
858
|
+
click: async () => {
|
|
859
|
+
await this.clear();
|
|
860
|
+
this._settingsModal!.closeModal();
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
label: 'Done',
|
|
865
|
+
variant: 'primary',
|
|
866
|
+
click: () => this._settingsModal!.closeModal()
|
|
867
|
+
}
|
|
868
|
+
]);
|
|
869
|
+
|
|
870
|
+
this._settingsModal.render(document.body);
|
|
871
|
+
this._settingsModal.open();
|
|
872
|
+
|
|
873
|
+
// Wire up import settings button
|
|
745
874
|
requestAnimationFrame(() => {
|
|
746
|
-
const
|
|
747
|
-
if (
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
settingsBtn.addEventListener('click', () => this._showReshapeModal());
|
|
753
|
-
statusEl.appendChild(settingsBtn);
|
|
875
|
+
const adjustBtn = document.getElementById(`${this._id}-adjust-import`);
|
|
876
|
+
if (adjustBtn) {
|
|
877
|
+
adjustBtn.addEventListener('click', () => {
|
|
878
|
+
this._settingsModal!.closeModal();
|
|
879
|
+
this._showReshapeModal();
|
|
880
|
+
});
|
|
754
881
|
}
|
|
755
882
|
});
|
|
756
883
|
}
|
|
@@ -758,23 +885,49 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
758
885
|
/**
|
|
759
886
|
* Update the collapsible summary text
|
|
760
887
|
*/
|
|
761
|
-
private _updateSummary(): void {
|
|
888
|
+
private _updateSummary(isMalformed?: boolean): void {
|
|
762
889
|
if (!this._collapsible || !this._detailsElement) return;
|
|
763
890
|
|
|
764
|
-
const
|
|
765
|
-
if (!
|
|
891
|
+
const summaryEl = this._detailsElement.querySelector('.jux-dataframe-summary');
|
|
892
|
+
if (!summaryEl) return;
|
|
766
893
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
894
|
+
const malformed = isMalformed ?? (this._df ? this._detectMalformedData(this._df) : false);
|
|
895
|
+
|
|
896
|
+
const summaryTextEl = summaryEl.querySelector('.jux-dataframe-summary-text');
|
|
897
|
+
if (summaryTextEl) {
|
|
898
|
+
if (!this._df) {
|
|
899
|
+
summaryTextEl.textContent = 'No data loaded';
|
|
900
|
+
} else if (this._summaryTemplate) {
|
|
901
|
+
summaryTextEl.textContent = this._summaryTemplate(this._df);
|
|
902
|
+
} else {
|
|
903
|
+
const suffix = malformed ? ' ⚠️' : '';
|
|
904
|
+
summaryTextEl.textContent = `${this.state.sourceName || 'Data'} — ${this._df.height} rows × ${this._df.width} cols${suffix}`;
|
|
905
|
+
}
|
|
770
906
|
}
|
|
771
907
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
908
|
+
// ✅ Add/update settings gear in summary
|
|
909
|
+
let gearBtn = summaryEl.querySelector('.jux-dataframe-summary-gear') as HTMLButtonElement;
|
|
910
|
+
|
|
911
|
+
if (this._df) {
|
|
912
|
+
if (!gearBtn) {
|
|
913
|
+
gearBtn = document.createElement('button');
|
|
914
|
+
gearBtn.className = 'jux-dataframe-summary-gear';
|
|
915
|
+
gearBtn.type = 'button';
|
|
916
|
+
gearBtn.innerHTML = '⚙️';
|
|
917
|
+
gearBtn.addEventListener('click', (e) => {
|
|
918
|
+
e.stopPropagation();
|
|
919
|
+
this._showSettingsModal();
|
|
920
|
+
});
|
|
921
|
+
summaryEl.appendChild(gearBtn);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (malformed) {
|
|
925
|
+
gearBtn.classList.add('jux-dataframe-gear-warning');
|
|
926
|
+
} else {
|
|
927
|
+
gearBtn.classList.remove('jux-dataframe-gear-warning');
|
|
928
|
+
}
|
|
929
|
+
} else if (gearBtn) {
|
|
930
|
+
gearBtn.remove();
|
|
778
931
|
}
|
|
779
932
|
}
|
|
780
933
|
|
|
@@ -782,6 +935,29 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
782
935
|
* MALFORMED DATA DETECTION
|
|
783
936
|
* ═══════════════════════════════════════════════════ */
|
|
784
937
|
|
|
938
|
+
private _appendSettingsButton(label: string, variant: string): void {
|
|
939
|
+
const statusEl = document.getElementById(`${this._id}-status`);
|
|
940
|
+
if (!statusEl) return;
|
|
941
|
+
|
|
942
|
+
// Remove existing settings button if any
|
|
943
|
+
const existing = statusEl.querySelector('.jux-dataframe-settings-btn');
|
|
944
|
+
if (existing) existing.remove();
|
|
945
|
+
|
|
946
|
+
const btn = document.createElement('button');
|
|
947
|
+
btn.className = `jux-dataframe-settings-btn jux-dataframe-settings-btn-${variant}`;
|
|
948
|
+
btn.type = 'button';
|
|
949
|
+
btn.textContent = `⚙️ ${label}`;
|
|
950
|
+
btn.style.marginLeft = '8px';
|
|
951
|
+
btn.style.cursor = 'pointer';
|
|
952
|
+
btn.style.fontSize = '0.8rem';
|
|
953
|
+
btn.style.padding = '2px 8px';
|
|
954
|
+
btn.style.borderRadius = 'var(--radius, 4px)';
|
|
955
|
+
btn.style.border = '1px solid hsl(var(--border))';
|
|
956
|
+
btn.style.background = variant === 'warning' ? 'hsl(38 92% 50% / 0.15)' : 'transparent';
|
|
957
|
+
btn.addEventListener('click', () => this._showReshapeModal());
|
|
958
|
+
statusEl.appendChild(btn);
|
|
959
|
+
}
|
|
960
|
+
|
|
785
961
|
private _detectMalformedData(df: DataFrame): boolean {
|
|
786
962
|
const columns = df.columns;
|
|
787
963
|
const rows = df.toRows();
|
|
@@ -924,8 +1100,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
924
1100
|
const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
|
|
925
1101
|
if (nonEmpty.length < values.length * 0.5) continue;
|
|
926
1102
|
const nonNumeric = nonEmpty.filter(v => {
|
|
927
|
-
const
|
|
928
|
-
return isNaN(Number(
|
|
1103
|
+
const trimmed = v.trim();
|
|
1104
|
+
return isNaN(Number(trimmed)) && trimmed !== '';
|
|
929
1105
|
}).length;
|
|
930
1106
|
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
931
1107
|
selectedSheetRow = sheetRow;
|
|
@@ -1229,8 +1405,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1229
1405
|
if (className) wrapper.className += ` ${className}`;
|
|
1230
1406
|
if (style) wrapper.setAttribute('style', style);
|
|
1231
1407
|
|
|
1408
|
+
// ═══════════════════════════════════════════════════
|
|
1409
|
+
// UPLOAD AREA (shown initially, hidden after data loads)
|
|
1410
|
+
// ═══════════════════════════════════════════════════
|
|
1232
1411
|
if (this._inlineUpload) {
|
|
1233
|
-
// ✅ NEW: Enhanced upload area with better styling
|
|
1234
1412
|
const uploadArea = document.createElement('div');
|
|
1235
1413
|
uploadArea.className = 'jux-dataframe-upload-area';
|
|
1236
1414
|
uploadArea.id = `${this._id}-upload-area`;
|
|
@@ -1247,11 +1425,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1247
1425
|
const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
|
|
1248
1426
|
this._uploadRef = upload;
|
|
1249
1427
|
|
|
1250
|
-
// ✅ Handle file change AND file clear
|
|
1251
1428
|
this._pendingSource = async () => {
|
|
1252
1429
|
upload.bind('change', async (files: File[]) => {
|
|
1253
1430
|
if (!files || files.length === 0) {
|
|
1254
|
-
// File was cleared - reset the table
|
|
1255
1431
|
await this.clear();
|
|
1256
1432
|
return;
|
|
1257
1433
|
}
|
|
@@ -1264,12 +1440,12 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1264
1440
|
uploadContainer.id = `${this._id}-upload-container`;
|
|
1265
1441
|
uploadArea.appendChild(uploadContainer);
|
|
1266
1442
|
|
|
1267
|
-
//
|
|
1443
|
+
// Status bar (for loading/error states only)
|
|
1268
1444
|
if (this._showStatus) {
|
|
1269
1445
|
const statusBar = document.createElement('div');
|
|
1270
|
-
statusBar.className = 'jux-dataframe-status
|
|
1446
|
+
statusBar.className = 'jux-dataframe-status';
|
|
1271
1447
|
statusBar.id = `${this._id}-status`;
|
|
1272
|
-
statusBar.style.display = 'none';
|
|
1448
|
+
statusBar.style.display = 'none';
|
|
1273
1449
|
uploadArea.appendChild(statusBar);
|
|
1274
1450
|
}
|
|
1275
1451
|
|
|
@@ -1285,25 +1461,40 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1285
1461
|
upload.render(uploadContainer);
|
|
1286
1462
|
} else {
|
|
1287
1463
|
container.appendChild(wrapper);
|
|
1288
|
-
|
|
1289
|
-
// ✅ Status bar for non-upload mode
|
|
1290
|
-
if (this._showStatus) {
|
|
1291
|
-
const statusBar = document.createElement('div');
|
|
1292
|
-
statusBar.className = 'jux-dataframe-status jux-dataframe-status-empty';
|
|
1293
|
-
statusBar.id = `${this._id}-status`;
|
|
1294
|
-
statusBar.textContent = 'No data loaded.';
|
|
1295
|
-
wrapper.appendChild(statusBar);
|
|
1296
|
-
}
|
|
1297
1464
|
}
|
|
1298
1465
|
|
|
1299
|
-
//
|
|
1466
|
+
// ═══════════════════════════════════════════════════
|
|
1467
|
+
// DATA CONTAINER (hidden initially, shown after data loads)
|
|
1468
|
+
// ═══════════════════════════════════════════════════
|
|
1469
|
+
const dataContainer = document.createElement('div');
|
|
1470
|
+
dataContainer.className = 'jux-dataframe-data';
|
|
1471
|
+
dataContainer.style.display = 'none'; // Hidden until data loads
|
|
1472
|
+
|
|
1473
|
+
// ✅ Toolbar with settings gear
|
|
1474
|
+
const toolbar = document.createElement('div');
|
|
1475
|
+
toolbar.className = 'jux-dataframe-toolbar';
|
|
1476
|
+
|
|
1477
|
+
const settingsGear = document.createElement('button');
|
|
1478
|
+
settingsGear.className = 'jux-dataframe-gear';
|
|
1479
|
+
settingsGear.id = `${this._id}-settings-gear`;
|
|
1480
|
+
settingsGear.type = 'button';
|
|
1481
|
+
settingsGear.innerHTML = '⚙️';
|
|
1482
|
+
settingsGear.title = 'Data Settings';
|
|
1483
|
+
settingsGear.addEventListener('click', () => this._showSettingsModal());
|
|
1484
|
+
toolbar.appendChild(settingsGear);
|
|
1485
|
+
|
|
1486
|
+
dataContainer.appendChild(toolbar);
|
|
1487
|
+
|
|
1488
|
+
// ═══════════════════════════════════════════════════
|
|
1489
|
+
// TABLE CONTAINER (inside data container or collapsible)
|
|
1490
|
+
// ═══════════════════════════════════════════════════
|
|
1300
1491
|
let tableContainer: HTMLElement;
|
|
1301
1492
|
|
|
1302
1493
|
if (this._collapsible) {
|
|
1303
1494
|
const details = document.createElement('details');
|
|
1304
1495
|
details.className = 'jux-dataframe-details';
|
|
1305
1496
|
details.open = !this._collapsed;
|
|
1306
|
-
details.style.display = 'none'; //
|
|
1497
|
+
details.style.display = 'none'; // Hidden until data loads
|
|
1307
1498
|
this._detailsElement = details;
|
|
1308
1499
|
|
|
1309
1500
|
const summary = document.createElement('summary');
|
|
@@ -1316,7 +1507,6 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1316
1507
|
|
|
1317
1508
|
const textSpan = document.createElement('span');
|
|
1318
1509
|
textSpan.className = 'jux-dataframe-summary-text';
|
|
1319
|
-
// Don't set text - will be set when data loads
|
|
1320
1510
|
summary.appendChild(textSpan);
|
|
1321
1511
|
|
|
1322
1512
|
details.appendChild(summary);
|
|
@@ -1325,16 +1515,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1325
1515
|
content.className = 'jux-dataframe-details-content';
|
|
1326
1516
|
details.appendChild(content);
|
|
1327
1517
|
|
|
1328
|
-
|
|
1518
|
+
dataContainer.appendChild(details);
|
|
1329
1519
|
tableContainer = content;
|
|
1330
1520
|
|
|
1331
1521
|
details.addEventListener('toggle', () => {
|
|
1332
1522
|
this._collapsed = !details.open;
|
|
1333
1523
|
});
|
|
1334
1524
|
} else {
|
|
1335
|
-
tableContainer =
|
|
1525
|
+
tableContainer = dataContainer;
|
|
1336
1526
|
}
|
|
1337
1527
|
|
|
1528
|
+
wrapper.appendChild(dataContainer);
|
|
1529
|
+
|
|
1530
|
+
// ═══════════════════════════════════════════════════
|
|
1531
|
+
// TABLE
|
|
1532
|
+
// ═══════════════════════════════════════════════════
|
|
1338
1533
|
const tbl = new Table(`${this._id}-table`, {
|
|
1339
1534
|
striped: this._tableOptions.striped,
|
|
1340
1535
|
hoverable: this._tableOptions.hoverable,
|
package/lib/styles/shadcn.css
CHANGED
|
@@ -916,6 +916,36 @@ code {
|
|
|
916
916
|
flex: 1;
|
|
917
917
|
}
|
|
918
918
|
|
|
919
|
+
/* ✅ Settings button in summary */
|
|
920
|
+
.jux-dataframe-summary-settings {
|
|
921
|
+
padding: 0.25rem 0.5rem;
|
|
922
|
+
font-size: 0.75rem;
|
|
923
|
+
font-weight: 500;
|
|
924
|
+
border: 1px solid hsl(var(--border));
|
|
925
|
+
border-radius: calc(var(--radius) - 2px);
|
|
926
|
+
background: hsl(var(--background));
|
|
927
|
+
color: hsl(var(--muted-foreground));
|
|
928
|
+
cursor: pointer;
|
|
929
|
+
transition: all 0.15s;
|
|
930
|
+
margin-left: auto;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
.jux-dataframe-summary-settings:hover {
|
|
934
|
+
background: hsl(var(--accent));
|
|
935
|
+
color: hsl(var(--foreground));
|
|
936
|
+
border-color: hsl(var(--muted-foreground) / 0.3);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.jux-dataframe-summary-settings-warning {
|
|
940
|
+
background: hsl(var(--warning) / 0.1);
|
|
941
|
+
border-color: hsl(var(--warning) / 0.3);
|
|
942
|
+
color: hsl(var(--warning-foreground));
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.jux-dataframe-summary-settings-warning:hover {
|
|
946
|
+
background: hsl(var(--warning) / 0.2);
|
|
947
|
+
}
|
|
948
|
+
|
|
919
949
|
.jux-dataframe-details-content {
|
|
920
950
|
padding: 0;
|
|
921
951
|
}
|
|
@@ -924,4 +954,96 @@ code {
|
|
|
924
954
|
.jux-dataframe-details-content .jux-table-wrapper {
|
|
925
955
|
border: none;
|
|
926
956
|
border-radius: 0;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
960
|
+
* DATAFRAME DATA VIEW (Post-upload clean UI)
|
|
961
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
962
|
+
|
|
963
|
+
.jux-dataframe-data {
|
|
964
|
+
display: flex;
|
|
965
|
+
flex-direction: column;
|
|
966
|
+
gap: 0.5rem;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.jux-dataframe-toolbar {
|
|
970
|
+
display: flex;
|
|
971
|
+
justify-content: flex-end;
|
|
972
|
+
align-items: center;
|
|
973
|
+
padding: 0.25rem 0;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.jux-dataframe-gear {
|
|
977
|
+
display: inline-flex;
|
|
978
|
+
align-items: center;
|
|
979
|
+
justify-content: center;
|
|
980
|
+
width: 2rem;
|
|
981
|
+
height: 2rem;
|
|
982
|
+
padding: 0;
|
|
983
|
+
border: none;
|
|
984
|
+
background: transparent;
|
|
985
|
+
border-radius: var(--radius);
|
|
986
|
+
cursor: pointer;
|
|
987
|
+
font-size: 1rem;
|
|
988
|
+
color: hsl(var(--muted-foreground));
|
|
989
|
+
transition: all 0.15s;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
.jux-dataframe-gear:hover {
|
|
993
|
+
background: hsl(var(--muted) / 0.5);
|
|
994
|
+
color: hsl(var(--foreground));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.jux-dataframe-gear-warning {
|
|
998
|
+
color: hsl(var(--warning));
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.jux-dataframe-gear-warning:hover {
|
|
1002
|
+
background: hsl(var(--warning) / 0.1);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/* Settings modal content */
|
|
1006
|
+
.jux-dataframe-settings-content {
|
|
1007
|
+
display: flex;
|
|
1008
|
+
flex-direction: column;
|
|
1009
|
+
gap: 1rem;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.jux-dataframe-settings-section {
|
|
1013
|
+
display: flex;
|
|
1014
|
+
flex-direction: column;
|
|
1015
|
+
gap: 0.25rem;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
.jux-dataframe-settings-label {
|
|
1019
|
+
font-size: 0.75rem;
|
|
1020
|
+
font-weight: 600;
|
|
1021
|
+
text-transform: uppercase;
|
|
1022
|
+
letter-spacing: 0.05em;
|
|
1023
|
+
color: hsl(var(--muted-foreground));
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
.jux-dataframe-settings-value {
|
|
1027
|
+
font-size: 0.875rem;
|
|
1028
|
+
color: hsl(var(--foreground));
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/* Summary gear in collapsible mode */
|
|
1032
|
+
.jux-dataframe-summary-gear {
|
|
1033
|
+
padding: 0.25rem 0.5rem;
|
|
1034
|
+
font-size: 0.875rem;
|
|
1035
|
+
border: none;
|
|
1036
|
+
background: transparent;
|
|
1037
|
+
cursor: pointer;
|
|
1038
|
+
color: hsl(var(--muted-foreground));
|
|
1039
|
+
transition: all 0.15s;
|
|
1040
|
+
margin-left: auto;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.jux-dataframe-summary-gear:hover {
|
|
1044
|
+
color: hsl(var(--foreground));
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
.jux-dataframe-summary-gear.jux-dataframe-gear-warning {
|
|
1048
|
+
color: hsl(var(--warning));
|
|
927
1049
|
}
|