juxscript 1.1.225 → 1.1.227
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 +18 -1
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +221 -90
- package/lib/components/dataframe.ts +246 -100
- package/lib/styles/shadcn.css +99 -9
- 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,7 +173,23 @@ 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
|
|
177
|
+
*/
|
|
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;
|
|
191
|
+
/**
|
|
192
|
+
* Update the collapsible summary text
|
|
176
193
|
*/
|
|
177
194
|
private _updateSummary;
|
|
178
195
|
private _appendSettingsButton;
|
|
@@ -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,36 +593,155 @@ 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
|
-
|
|
593
|
-
|
|
596
|
+
// ✅ Show the table container, hide upload area
|
|
597
|
+
this._showDataView();
|
|
598
|
+
// ✅ Update collapsible summary if enabled
|
|
594
599
|
if (this._collapsible && this._detailsElement) {
|
|
595
600
|
this._detailsElement.style.display = '';
|
|
596
|
-
this._updateSummary(
|
|
601
|
+
this._updateSummary();
|
|
597
602
|
}
|
|
598
|
-
// ✅
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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;
|
|
652
|
+
const isMalformed = this._detectMalformedData(this._df);
|
|
653
|
+
if (isMalformed) {
|
|
654
|
+
gear.classList.add('jux-dataframe-gear-warning');
|
|
655
|
+
gear.title = `${this.state.sourceName} — May need reformatting`;
|
|
610
656
|
}
|
|
611
657
|
else {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (statusEl) {
|
|
615
|
-
statusEl.style.display = 'none';
|
|
616
|
-
}
|
|
658
|
+
gear.classList.remove('jux-dataframe-gear-warning');
|
|
659
|
+
gear.title = `${this.state.sourceName} — ${this._df.height} rows × ${this._df.width} cols`;
|
|
617
660
|
}
|
|
618
|
-
this._triggerCallback('load', this._df, null, this);
|
|
619
661
|
}
|
|
620
662
|
/**
|
|
621
|
-
*
|
|
663
|
+
* Show the unified settings modal
|
|
664
|
+
*/
|
|
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
|
|
733
|
+
requestAnimationFrame(() => {
|
|
734
|
+
const adjustBtn = document.getElementById(`${this._id}-adjust-import`);
|
|
735
|
+
if (adjustBtn) {
|
|
736
|
+
adjustBtn.addEventListener('click', () => {
|
|
737
|
+
this._settingsModal.closeModal();
|
|
738
|
+
this._showReshapeModal();
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Update the collapsible summary text
|
|
622
745
|
*/
|
|
623
746
|
_updateSummary(isMalformed) {
|
|
624
747
|
if (!this._collapsible || !this._detailsElement)
|
|
@@ -626,9 +749,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
626
749
|
const summaryEl = this._detailsElement.querySelector('.jux-dataframe-summary');
|
|
627
750
|
if (!summaryEl)
|
|
628
751
|
return;
|
|
629
|
-
// Get or detect malformed status
|
|
630
752
|
const malformed = isMalformed ?? (this._df ? this._detectMalformedData(this._df) : false);
|
|
631
|
-
// Update text
|
|
632
753
|
const summaryTextEl = summaryEl.querySelector('.jux-dataframe-summary-text');
|
|
633
754
|
if (summaryTextEl) {
|
|
634
755
|
if (!this._df) {
|
|
@@ -642,32 +763,29 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
642
763
|
summaryTextEl.textContent = `${this.state.sourceName || 'Data'} — ${this._df.height} rows × ${this._df.width} cols${suffix}`;
|
|
643
764
|
}
|
|
644
765
|
}
|
|
645
|
-
// ✅ Add settings
|
|
646
|
-
let
|
|
647
|
-
if (this.
|
|
648
|
-
if (!
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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();
|
|
655
777
|
});
|
|
656
|
-
summaryEl.appendChild(
|
|
778
|
+
summaryEl.appendChild(gearBtn);
|
|
657
779
|
}
|
|
658
|
-
// Update button text/style based on malformed status
|
|
659
780
|
if (malformed) {
|
|
660
|
-
|
|
661
|
-
settingsBtn.className = 'jux-dataframe-summary-settings jux-dataframe-summary-settings-warning';
|
|
781
|
+
gearBtn.classList.add('jux-dataframe-gear-warning');
|
|
662
782
|
}
|
|
663
783
|
else {
|
|
664
|
-
|
|
665
|
-
settingsBtn.className = 'jux-dataframe-summary-settings';
|
|
784
|
+
gearBtn.classList.remove('jux-dataframe-gear-warning');
|
|
666
785
|
}
|
|
667
786
|
}
|
|
668
|
-
else if (
|
|
669
|
-
|
|
670
|
-
settingsBtn.remove();
|
|
787
|
+
else if (gearBtn) {
|
|
788
|
+
gearBtn.remove();
|
|
671
789
|
}
|
|
672
790
|
}
|
|
673
791
|
/* ═══════════════════════════════════════════════════
|
|
@@ -817,8 +935,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
817
935
|
if (nonEmpty.length < values.length * 0.5)
|
|
818
936
|
continue;
|
|
819
937
|
const nonNumeric = nonEmpty.filter(v => {
|
|
820
|
-
const
|
|
821
|
-
return isNaN(Number(
|
|
938
|
+
const trimmed = v.trim();
|
|
939
|
+
return isNaN(Number(trimmed)) && trimmed !== '';
|
|
822
940
|
}).length;
|
|
823
941
|
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
824
942
|
selectedSheetRow = sheetRow;
|
|
@@ -1091,8 +1209,10 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1091
1209
|
wrapper.className += ` ${className}`;
|
|
1092
1210
|
if (style)
|
|
1093
1211
|
wrapper.setAttribute('style', style);
|
|
1212
|
+
// ═══════════════════════════════════════════════════
|
|
1213
|
+
// UPLOAD AREA (shown initially, hidden after data loads)
|
|
1214
|
+
// ═══════════════════════════════════════════════════
|
|
1094
1215
|
if (this._inlineUpload) {
|
|
1095
|
-
// ✅ NEW: Enhanced upload area with better styling
|
|
1096
1216
|
const uploadArea = document.createElement('div');
|
|
1097
1217
|
uploadArea.className = 'jux-dataframe-upload-area';
|
|
1098
1218
|
uploadArea.id = `${this._id}-upload-area`;
|
|
@@ -1106,32 +1226,27 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1106
1226
|
}
|
|
1107
1227
|
const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
|
|
1108
1228
|
this._uploadRef = upload;
|
|
1109
|
-
// ✅ Handle file change AND file clear
|
|
1110
1229
|
this._pendingSource = async () => {
|
|
1111
1230
|
upload.bind('change', async (files) => {
|
|
1112
1231
|
if (!files || files.length === 0) {
|
|
1113
|
-
// File was cleared - reset the table
|
|
1114
1232
|
await this.clear();
|
|
1115
1233
|
return;
|
|
1116
1234
|
}
|
|
1117
1235
|
await this._handleFile(files[0]);
|
|
1118
1236
|
});
|
|
1119
1237
|
};
|
|
1120
|
-
const uploadRow = document.createElement('div');
|
|
1121
|
-
uploadRow.className = 'jux-dataframe-upload-row';
|
|
1122
1238
|
const uploadContainer = document.createElement('div');
|
|
1123
1239
|
uploadContainer.className = 'jux-dataframe-upload';
|
|
1124
1240
|
uploadContainer.id = `${this._id}-upload-container`;
|
|
1125
|
-
|
|
1126
|
-
//
|
|
1241
|
+
uploadArea.appendChild(uploadContainer);
|
|
1242
|
+
// Status bar (for loading/error states only)
|
|
1127
1243
|
if (this._showStatus) {
|
|
1128
1244
|
const statusBar = document.createElement('div');
|
|
1129
|
-
statusBar.className = 'jux-dataframe-status
|
|
1245
|
+
statusBar.className = 'jux-dataframe-status';
|
|
1130
1246
|
statusBar.id = `${this._id}-status`;
|
|
1131
|
-
statusBar.style.display = 'none';
|
|
1132
|
-
|
|
1247
|
+
statusBar.style.display = 'none';
|
|
1248
|
+
uploadArea.appendChild(statusBar);
|
|
1133
1249
|
}
|
|
1134
|
-
uploadArea.appendChild(uploadRow);
|
|
1135
1250
|
if (this._uploadDescription) {
|
|
1136
1251
|
const descEl = document.createElement('div');
|
|
1137
1252
|
descEl.className = 'jux-dataframe-upload-description';
|
|
@@ -1144,16 +1259,28 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1144
1259
|
}
|
|
1145
1260
|
else {
|
|
1146
1261
|
container.appendChild(wrapper);
|
|
1147
|
-
// Status bar for non-upload mode
|
|
1148
|
-
if (this._showStatus) {
|
|
1149
|
-
const statusBar = document.createElement('div');
|
|
1150
|
-
statusBar.className = 'jux-dataframe-status jux-dataframe-status-empty';
|
|
1151
|
-
statusBar.id = `${this._id}-status`;
|
|
1152
|
-
statusBar.textContent = 'No data loaded.';
|
|
1153
|
-
wrapper.appendChild(statusBar);
|
|
1154
|
-
}
|
|
1155
1262
|
}
|
|
1156
|
-
//
|
|
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
|
+
// ═══════════════════════════════════════════════════
|
|
1157
1284
|
let tableContainer;
|
|
1158
1285
|
if (this._collapsible) {
|
|
1159
1286
|
const details = document.createElement('details');
|
|
@@ -1174,15 +1301,19 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
1174
1301
|
const content = document.createElement('div');
|
|
1175
1302
|
content.className = 'jux-dataframe-details-content';
|
|
1176
1303
|
details.appendChild(content);
|
|
1177
|
-
|
|
1304
|
+
dataContainer.appendChild(details);
|
|
1178
1305
|
tableContainer = content;
|
|
1179
1306
|
details.addEventListener('toggle', () => {
|
|
1180
1307
|
this._collapsed = !details.open;
|
|
1181
1308
|
});
|
|
1182
1309
|
}
|
|
1183
1310
|
else {
|
|
1184
|
-
tableContainer =
|
|
1311
|
+
tableContainer = dataContainer;
|
|
1185
1312
|
}
|
|
1313
|
+
wrapper.appendChild(dataContainer);
|
|
1314
|
+
// ═══════════════════════════════════════════════════
|
|
1315
|
+
// TABLE
|
|
1316
|
+
// ═══════════════════════════════════════════════════
|
|
1186
1317
|
const tbl = new Table(`${this._id}-table`, {
|
|
1187
1318
|
striped: this._tableOptions.striped,
|
|
1188
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,44 +715,175 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
710
715
|
this._table.columns(columnDefs).rows(this._df.toRows());
|
|
711
716
|
}
|
|
712
717
|
|
|
713
|
-
|
|
718
|
+
// ✅ Show the table container, hide upload area
|
|
719
|
+
this._showDataView();
|
|
714
720
|
|
|
715
|
-
// ✅
|
|
721
|
+
// ✅ Update collapsible summary if enabled
|
|
716
722
|
if (this._collapsible && this._detailsElement) {
|
|
717
723
|
this._detailsElement.style.display = '';
|
|
718
|
-
this._updateSummary(
|
|
724
|
+
this._updateSummary();
|
|
719
725
|
}
|
|
720
726
|
|
|
721
|
-
// ✅
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
727
|
+
// ✅ Hide status - data is loaded
|
|
728
|
+
this._updateStatus('', 'success');
|
|
729
|
+
|
|
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';
|
|
744
|
+
}
|
|
745
|
+
|
|
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();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Hide data view, show upload area
|
|
758
|
+
*/
|
|
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`;
|
|
738
788
|
} else {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if (statusEl) {
|
|
742
|
-
statusEl.style.display = 'none';
|
|
743
|
-
}
|
|
789
|
+
gear.classList.remove('jux-dataframe-gear-warning');
|
|
790
|
+
gear.title = `${this.state.sourceName} — ${this._df.height} rows × ${this._df.width} cols`;
|
|
744
791
|
}
|
|
792
|
+
}
|
|
745
793
|
|
|
746
|
-
|
|
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
|
|
874
|
+
requestAnimationFrame(() => {
|
|
875
|
+
const adjustBtn = document.getElementById(`${this._id}-adjust-import`);
|
|
876
|
+
if (adjustBtn) {
|
|
877
|
+
adjustBtn.addEventListener('click', () => {
|
|
878
|
+
this._settingsModal!.closeModal();
|
|
879
|
+
this._showReshapeModal();
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
});
|
|
747
883
|
}
|
|
748
884
|
|
|
749
885
|
/**
|
|
750
|
-
* Update the collapsible summary text
|
|
886
|
+
* Update the collapsible summary text
|
|
751
887
|
*/
|
|
752
888
|
private _updateSummary(isMalformed?: boolean): void {
|
|
753
889
|
if (!this._collapsible || !this._detailsElement) return;
|
|
@@ -755,10 +891,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
755
891
|
const summaryEl = this._detailsElement.querySelector('.jux-dataframe-summary');
|
|
756
892
|
if (!summaryEl) return;
|
|
757
893
|
|
|
758
|
-
// Get or detect malformed status
|
|
759
894
|
const malformed = isMalformed ?? (this._df ? this._detectMalformedData(this._df) : false);
|
|
760
895
|
|
|
761
|
-
// Update text
|
|
762
896
|
const summaryTextEl = summaryEl.querySelector('.jux-dataframe-summary-text');
|
|
763
897
|
if (summaryTextEl) {
|
|
764
898
|
if (!this._df) {
|
|
@@ -771,32 +905,29 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
771
905
|
}
|
|
772
906
|
}
|
|
773
907
|
|
|
774
|
-
// ✅ Add settings
|
|
775
|
-
let
|
|
776
|
-
|
|
777
|
-
if (this.
|
|
778
|
-
if (!
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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();
|
|
785
920
|
});
|
|
786
|
-
summaryEl.appendChild(
|
|
921
|
+
summaryEl.appendChild(gearBtn);
|
|
787
922
|
}
|
|
788
923
|
|
|
789
|
-
// Update button text/style based on malformed status
|
|
790
924
|
if (malformed) {
|
|
791
|
-
|
|
792
|
-
settingsBtn.className = 'jux-dataframe-summary-settings jux-dataframe-summary-settings-warning';
|
|
925
|
+
gearBtn.classList.add('jux-dataframe-gear-warning');
|
|
793
926
|
} else {
|
|
794
|
-
|
|
795
|
-
settingsBtn.className = 'jux-dataframe-summary-settings';
|
|
927
|
+
gearBtn.classList.remove('jux-dataframe-gear-warning');
|
|
796
928
|
}
|
|
797
|
-
} else if (
|
|
798
|
-
|
|
799
|
-
settingsBtn.remove();
|
|
929
|
+
} else if (gearBtn) {
|
|
930
|
+
gearBtn.remove();
|
|
800
931
|
}
|
|
801
932
|
}
|
|
802
933
|
|
|
@@ -969,8 +1100,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
969
1100
|
const nonEmpty = values.filter(v => v !== null && v !== undefined && String(v).trim() !== '');
|
|
970
1101
|
if (nonEmpty.length < values.length * 0.5) continue;
|
|
971
1102
|
const nonNumeric = nonEmpty.filter(v => {
|
|
972
|
-
const
|
|
973
|
-
return isNaN(Number(
|
|
1103
|
+
const trimmed = v.trim();
|
|
1104
|
+
return isNaN(Number(trimmed)) && trimmed !== '';
|
|
974
1105
|
}).length;
|
|
975
1106
|
if (nonNumeric >= nonEmpty.length * 0.7) {
|
|
976
1107
|
selectedSheetRow = sheetRow;
|
|
@@ -1274,8 +1405,10 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1274
1405
|
if (className) wrapper.className += ` ${className}`;
|
|
1275
1406
|
if (style) wrapper.setAttribute('style', style);
|
|
1276
1407
|
|
|
1408
|
+
// ═══════════════════════════════════════════════════
|
|
1409
|
+
// UPLOAD AREA (shown initially, hidden after data loads)
|
|
1410
|
+
// ═══════════════════════════════════════════════════
|
|
1277
1411
|
if (this._inlineUpload) {
|
|
1278
|
-
// ✅ NEW: Enhanced upload area with better styling
|
|
1279
1412
|
const uploadArea = document.createElement('div');
|
|
1280
1413
|
uploadArea.className = 'jux-dataframe-upload-area';
|
|
1281
1414
|
uploadArea.id = `${this._id}-upload-area`;
|
|
@@ -1292,11 +1425,9 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1292
1425
|
const upload = new FileUpload(`${this._id}-upload`, uploadOpts);
|
|
1293
1426
|
this._uploadRef = upload;
|
|
1294
1427
|
|
|
1295
|
-
// ✅ Handle file change AND file clear
|
|
1296
1428
|
this._pendingSource = async () => {
|
|
1297
1429
|
upload.bind('change', async (files: File[]) => {
|
|
1298
1430
|
if (!files || files.length === 0) {
|
|
1299
|
-
// File was cleared - reset the table
|
|
1300
1431
|
await this.clear();
|
|
1301
1432
|
return;
|
|
1302
1433
|
}
|
|
@@ -1304,25 +1435,20 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1304
1435
|
});
|
|
1305
1436
|
};
|
|
1306
1437
|
|
|
1307
|
-
const uploadRow = document.createElement('div');
|
|
1308
|
-
uploadRow.className = 'jux-dataframe-upload-row';
|
|
1309
|
-
|
|
1310
1438
|
const uploadContainer = document.createElement('div');
|
|
1311
1439
|
uploadContainer.className = 'jux-dataframe-upload';
|
|
1312
1440
|
uploadContainer.id = `${this._id}-upload-container`;
|
|
1313
|
-
|
|
1441
|
+
uploadArea.appendChild(uploadContainer);
|
|
1314
1442
|
|
|
1315
|
-
//
|
|
1443
|
+
// Status bar (for loading/error states only)
|
|
1316
1444
|
if (this._showStatus) {
|
|
1317
1445
|
const statusBar = document.createElement('div');
|
|
1318
|
-
statusBar.className = 'jux-dataframe-status
|
|
1446
|
+
statusBar.className = 'jux-dataframe-status';
|
|
1319
1447
|
statusBar.id = `${this._id}-status`;
|
|
1320
|
-
statusBar.style.display = 'none';
|
|
1321
|
-
|
|
1448
|
+
statusBar.style.display = 'none';
|
|
1449
|
+
uploadArea.appendChild(statusBar);
|
|
1322
1450
|
}
|
|
1323
1451
|
|
|
1324
|
-
uploadArea.appendChild(uploadRow);
|
|
1325
|
-
|
|
1326
1452
|
if (this._uploadDescription) {
|
|
1327
1453
|
const descEl = document.createElement('div');
|
|
1328
1454
|
descEl.className = 'jux-dataframe-upload-description';
|
|
@@ -1335,18 +1461,33 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1335
1461
|
upload.render(uploadContainer);
|
|
1336
1462
|
} else {
|
|
1337
1463
|
container.appendChild(wrapper);
|
|
1338
|
-
|
|
1339
|
-
// Status bar for non-upload mode
|
|
1340
|
-
if (this._showStatus) {
|
|
1341
|
-
const statusBar = document.createElement('div');
|
|
1342
|
-
statusBar.className = 'jux-dataframe-status jux-dataframe-status-empty';
|
|
1343
|
-
statusBar.id = `${this._id}-status`;
|
|
1344
|
-
statusBar.textContent = 'No data loaded.';
|
|
1345
|
-
wrapper.appendChild(statusBar);
|
|
1346
|
-
}
|
|
1347
1464
|
}
|
|
1348
1465
|
|
|
1349
|
-
//
|
|
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
|
+
// ═══════════════════════════════════════════════════
|
|
1350
1491
|
let tableContainer: HTMLElement;
|
|
1351
1492
|
|
|
1352
1493
|
if (this._collapsible) {
|
|
@@ -1374,16 +1515,21 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
1374
1515
|
content.className = 'jux-dataframe-details-content';
|
|
1375
1516
|
details.appendChild(content);
|
|
1376
1517
|
|
|
1377
|
-
|
|
1518
|
+
dataContainer.appendChild(details);
|
|
1378
1519
|
tableContainer = content;
|
|
1379
1520
|
|
|
1380
1521
|
details.addEventListener('toggle', () => {
|
|
1381
1522
|
this._collapsed = !details.open;
|
|
1382
1523
|
});
|
|
1383
1524
|
} else {
|
|
1384
|
-
tableContainer =
|
|
1525
|
+
tableContainer = dataContainer;
|
|
1385
1526
|
}
|
|
1386
1527
|
|
|
1528
|
+
wrapper.appendChild(dataContainer);
|
|
1529
|
+
|
|
1530
|
+
// ═══════════════════════════════════════════════════
|
|
1531
|
+
// TABLE
|
|
1532
|
+
// ═══════════════════════════════════════════════════
|
|
1387
1533
|
const tbl = new Table(`${this._id}-table`, {
|
|
1388
1534
|
striped: this._tableOptions.striped,
|
|
1389
1535
|
hoverable: this._tableOptions.hoverable,
|
package/lib/styles/shadcn.css
CHANGED
|
@@ -186,22 +186,20 @@ p { line-height: 1.75; color: hsl(var(--foreground)); }
|
|
|
186
186
|
flex-direction: column;
|
|
187
187
|
align-items: flex-start;
|
|
188
188
|
gap: 0.5rem;
|
|
189
|
-
padding:
|
|
190
|
-
background:
|
|
191
|
-
border:
|
|
192
|
-
border-radius:
|
|
189
|
+
padding: 0.5rem;
|
|
190
|
+
background: transparent;
|
|
191
|
+
border: none;
|
|
192
|
+
border-radius: 0;
|
|
193
193
|
transition: all 0.2s;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
.jux-dataframe-upload-area:hover {
|
|
197
|
-
background:
|
|
198
|
-
border-color: hsl(var(--muted-foreground) / 0.3);
|
|
197
|
+
background: transparent;
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
.jux-dataframe-upload-area:has(.jux-fileupload-filelist:not(:empty)) {
|
|
202
|
-
background:
|
|
203
|
-
border
|
|
204
|
-
border-style: solid;
|
|
201
|
+
background: transparent;
|
|
202
|
+
border: none;
|
|
205
203
|
}
|
|
206
204
|
|
|
207
205
|
.jux-dataframe-upload {
|
|
@@ -954,4 +952,96 @@ code {
|
|
|
954
952
|
.jux-dataframe-details-content .jux-table-wrapper {
|
|
955
953
|
border: none;
|
|
956
954
|
border-radius: 0;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
958
|
+
* DATAFRAME DATA VIEW (Post-upload clean UI)
|
|
959
|
+
* ═══════════════════════════════════════════════════════════════════ */
|
|
960
|
+
|
|
961
|
+
.jux-dataframe-data {
|
|
962
|
+
display: flex;
|
|
963
|
+
flex-direction: column;
|
|
964
|
+
gap: 0.5rem;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.jux-dataframe-toolbar {
|
|
968
|
+
display: flex;
|
|
969
|
+
justify-content: flex-end;
|
|
970
|
+
align-items: center;
|
|
971
|
+
padding: 0.25rem 0;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.jux-dataframe-gear {
|
|
975
|
+
display: inline-flex;
|
|
976
|
+
align-items: center;
|
|
977
|
+
justify-content: center;
|
|
978
|
+
width: 2rem;
|
|
979
|
+
height: 2rem;
|
|
980
|
+
padding: 0;
|
|
981
|
+
border: none;
|
|
982
|
+
background: transparent;
|
|
983
|
+
border-radius: var(--radius);
|
|
984
|
+
cursor: pointer;
|
|
985
|
+
font-size: 1rem;
|
|
986
|
+
color: hsl(var(--muted-foreground));
|
|
987
|
+
transition: all 0.15s;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.jux-dataframe-gear:hover {
|
|
991
|
+
background: hsl(var(--muted) / 0.5);
|
|
992
|
+
color: hsl(var(--foreground));
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.jux-dataframe-gear-warning {
|
|
996
|
+
color: hsl(var(--warning));
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.jux-dataframe-gear-warning:hover {
|
|
1000
|
+
background: hsl(var(--warning) / 0.1);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/* Settings modal content */
|
|
1004
|
+
.jux-dataframe-settings-content {
|
|
1005
|
+
display: flex;
|
|
1006
|
+
flex-direction: column;
|
|
1007
|
+
gap: 1rem;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.jux-dataframe-settings-section {
|
|
1011
|
+
display: flex;
|
|
1012
|
+
flex-direction: column;
|
|
1013
|
+
gap: 0.25rem;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
.jux-dataframe-settings-label {
|
|
1017
|
+
font-size: 0.75rem;
|
|
1018
|
+
font-weight: 600;
|
|
1019
|
+
text-transform: uppercase;
|
|
1020
|
+
letter-spacing: 0.05em;
|
|
1021
|
+
color: hsl(var(--muted-foreground));
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
.jux-dataframe-settings-value {
|
|
1025
|
+
font-size: 0.875rem;
|
|
1026
|
+
color: hsl(var(--foreground));
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/* Summary gear in collapsible mode */
|
|
1030
|
+
.jux-dataframe-summary-gear {
|
|
1031
|
+
padding: 0.25rem 0.5rem;
|
|
1032
|
+
font-size: 0.875rem;
|
|
1033
|
+
border: none;
|
|
1034
|
+
background: transparent;
|
|
1035
|
+
cursor: pointer;
|
|
1036
|
+
color: hsl(var(--muted-foreground));
|
|
1037
|
+
transition: all 0.15s;
|
|
1038
|
+
margin-left: auto;
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.jux-dataframe-summary-gear:hover {
|
|
1042
|
+
color: hsl(var(--foreground));
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.jux-dataframe-summary-gear.jux-dataframe-gear-warning {
|
|
1046
|
+
color: hsl(var(--warning));
|
|
957
1047
|
}
|