juxscript 1.1.193 → 1.1.195
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.map +1 -1
- package/lib/components/dataframe.js +163 -49
- package/lib/components/dataframe.ts +186 -54
- package/lib/styles/shadcn.css +30 -32
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAQnC,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,SAAS,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,aAAa,CAAC,cAAc,CAAC;IACjE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,mBAAmB,CAAiB;IAC5C,OAAO,CAAC,YAAY,CAAiE;IACrF,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,qBAAqB,CAAkB;gBAEnC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAmCtD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAC/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAMhD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAwB9B,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAWpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI;IAiBnE,UAAU,CAAC,KAAK,GAAE,MAAsB,EAAE,MAAM,GAAE,MAAoC,EAAE,IAAI,GAAE,MAAiB,GAAG,IAAI;IAStH,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAM3B,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,SAAS,GAAG,IAAI;IAQ7C,MAAM,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI;IAI7E,MAAM,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;IAI/B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI;IAI7C,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,IAAI,CAAC,CAAC,GAAE,MAAU,GAAG,IAAI;IAIzB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,GAAG,IAAI;IAIpF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAQxH,IAAI,EAAE,IAAI,SAAS,GAAG,IAAI,CAAqB;IAC/C,IAAI,MAAM,IAAI,aAAa,CAAyB;IACpD,IAAI,KAAK,IAAI,KAAK,GAAG,IAAI,CAAwB;IACjD,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IACtC,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IACjC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE;IAC/B,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAsC;IACnE,IAAI,OAAO,IAAI,MAAM,EAAE,CAAoC;IAErD,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUhD,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IACzB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC1B,UAAU,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC5B,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI;IAC3B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC5B,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC7B,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/B,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;YAMf,WAAW;IAyDzB,OAAO,CAAC,iBAAiB;IAyJzB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAC,oBAAoB;IA6B5B,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,oBAAoB;YASd,sBAAsB;IAuLpC,OAAO,CAAC,oBAAoB;IAyM5B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAExC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;CAoErE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,CAExF"}
|
|
@@ -3,7 +3,6 @@ import { DataFrame } from '../storage/DataFrame.js';
|
|
|
3
3
|
import { TabularDriver } from '../storage/TabularDriver.js';
|
|
4
4
|
import { FileUpload } from './fileupload.js';
|
|
5
5
|
import { Table } from './table.js';
|
|
6
|
-
import { Tabs } from './tabs.js';
|
|
7
6
|
import { Modal } from './modal.js';
|
|
8
7
|
import { renderIcon } from './icons.js';
|
|
9
8
|
const TRIGGER_EVENTS = [];
|
|
@@ -253,25 +252,62 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
253
252
|
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
254
253
|
if (existingTable)
|
|
255
254
|
existingTable.remove();
|
|
255
|
+
const existingTabs = wrapper.querySelector('.jux-dataframe-tabs');
|
|
256
|
+
if (existingTabs)
|
|
257
|
+
existingTabs.remove();
|
|
256
258
|
Object.entries(sheets).forEach(([name, df]) => {
|
|
257
259
|
this._sheets.set(name, df);
|
|
258
260
|
});
|
|
259
261
|
const sheetNames = Object.keys(sheets);
|
|
260
|
-
|
|
261
|
-
tabs: sheetNames.map(name => ({
|
|
262
|
-
id: name,
|
|
263
|
-
label: name,
|
|
264
|
-
content: ''
|
|
265
|
-
})),
|
|
266
|
-
activeTab: sheetNames[0]
|
|
267
|
-
});
|
|
268
|
-
this._tabs = tabs;
|
|
262
|
+
// Build custom tab bar with settings cogs
|
|
269
263
|
const tabsContainer = document.createElement('div');
|
|
270
264
|
tabsContainer.className = 'jux-dataframe-tabs';
|
|
265
|
+
const tabList = document.createElement('div');
|
|
266
|
+
tabList.className = 'jux-tabs-list';
|
|
267
|
+
sheetNames.forEach((sheetName, idx) => {
|
|
268
|
+
const tabBtn = document.createElement('button');
|
|
269
|
+
tabBtn.className = 'jux-tabs-button' + (idx === 0 ? ' jux-tabs-button-active' : '');
|
|
270
|
+
tabBtn.setAttribute('data-sheet', sheetName);
|
|
271
|
+
const labelSpan = document.createElement('span');
|
|
272
|
+
labelSpan.textContent = sheetName;
|
|
273
|
+
tabBtn.appendChild(labelSpan);
|
|
274
|
+
// Add settings cog icon
|
|
275
|
+
const cogBtn = document.createElement('button');
|
|
276
|
+
cogBtn.className = 'jux-dataframe-tab-settings';
|
|
277
|
+
cogBtn.title = 'Import settings for ' + sheetName;
|
|
278
|
+
cogBtn.innerHTML = `<span class="iconify" data-icon="lucide:settings" style="width:14px;height:14px;"></span>`;
|
|
279
|
+
cogBtn.addEventListener('click', (e) => {
|
|
280
|
+
e.stopPropagation();
|
|
281
|
+
this._showReshapeModal();
|
|
282
|
+
});
|
|
283
|
+
tabBtn.appendChild(cogBtn);
|
|
284
|
+
tabBtn.addEventListener('click', () => {
|
|
285
|
+
// Switch active tab
|
|
286
|
+
tabList.querySelectorAll('.jux-tabs-button').forEach(btn => {
|
|
287
|
+
btn.classList.remove('jux-tabs-button-active');
|
|
288
|
+
});
|
|
289
|
+
tabBtn.classList.add('jux-tabs-button-active');
|
|
290
|
+
// Show/hide panels
|
|
291
|
+
wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
|
|
292
|
+
panel.style.display = 'none';
|
|
293
|
+
});
|
|
294
|
+
const panel = document.getElementById(`${this._id}-panel-${sheetName}`);
|
|
295
|
+
if (panel)
|
|
296
|
+
panel.style.display = 'block';
|
|
297
|
+
// Update current df reference
|
|
298
|
+
this._df = this._sheets.get(sheetName) || null;
|
|
299
|
+
});
|
|
300
|
+
tabList.appendChild(tabBtn);
|
|
301
|
+
});
|
|
302
|
+
tabsContainer.appendChild(tabList);
|
|
271
303
|
wrapper.appendChild(tabsContainer);
|
|
272
|
-
|
|
273
|
-
sheetNames.forEach(sheetName => {
|
|
304
|
+
// Create panels for each sheet
|
|
305
|
+
sheetNames.forEach((sheetName, idx) => {
|
|
274
306
|
const df = sheets[sheetName];
|
|
307
|
+
const panel = document.createElement('div');
|
|
308
|
+
panel.className = 'jux-tabs-panel';
|
|
309
|
+
panel.id = `${this._id}-panel-${sheetName}`;
|
|
310
|
+
panel.style.display = idx === 0 ? 'block' : 'none';
|
|
275
311
|
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
276
312
|
striped: this._tableOptions.striped,
|
|
277
313
|
hoverable: this._tableOptions.hoverable,
|
|
@@ -282,10 +318,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
282
318
|
});
|
|
283
319
|
const columnDefs = df.columns.map(col => ({ key: col, label: col }));
|
|
284
320
|
table.columns(columnDefs).rows(df.toRows());
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return;
|
|
288
|
-
table.render(tabPanel);
|
|
321
|
+
wrapper.appendChild(panel);
|
|
322
|
+
table.render(panel);
|
|
289
323
|
if (this._tableOptions.filterable) {
|
|
290
324
|
const filterContainer = document.createElement('div');
|
|
291
325
|
filterContainer.className = 'jux-dataframe-filter';
|
|
@@ -310,12 +344,18 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
310
344
|
const filtered = df.filter((row) => Object.values(row).some(v => v !== null && v !== undefined && String(v).toLowerCase().includes(text)));
|
|
311
345
|
table.rows(filtered.toRows());
|
|
312
346
|
});
|
|
313
|
-
const tableWrapper =
|
|
347
|
+
const tableWrapper = panel.querySelector('.jux-table-wrapper');
|
|
314
348
|
if (tableWrapper) {
|
|
315
|
-
|
|
349
|
+
panel.insertBefore(filterContainer, tableWrapper);
|
|
316
350
|
}
|
|
317
351
|
}
|
|
318
352
|
});
|
|
353
|
+
// Ensure Iconify renders the cog icons
|
|
354
|
+
requestAnimationFrame(() => {
|
|
355
|
+
if (window.Iconify) {
|
|
356
|
+
window.Iconify.scan();
|
|
357
|
+
}
|
|
358
|
+
});
|
|
319
359
|
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
320
360
|
this._updateStatus(`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`, 'success');
|
|
321
361
|
this._df = sheets[sheetNames[0]];
|
|
@@ -491,7 +531,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
491
531
|
</div>
|
|
492
532
|
<div class="jux-reshape-preview-container">
|
|
493
533
|
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
494
|
-
Preview
|
|
534
|
+
Preview
|
|
495
535
|
</div>
|
|
496
536
|
<div id="${this._id}-preview" class="jux-reshape-preview"></div>
|
|
497
537
|
</div>
|
|
@@ -543,35 +583,73 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
543
583
|
const updateHint = (headerRow) => {
|
|
544
584
|
if (!hintDiv)
|
|
545
585
|
return;
|
|
546
|
-
hintDiv.innerHTML =
|
|
547
|
-
`
|
|
586
|
+
hintDiv.innerHTML = headerRow > 0
|
|
587
|
+
? `Row <strong>${headerRow}</strong> will be used as column headers. ` +
|
|
588
|
+
`The <strong>${headerRow}</strong> row${headerRow > 1 ? 's' : ''} above will be skipped.`
|
|
589
|
+
: `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
548
590
|
};
|
|
549
591
|
const updatePreview = async () => {
|
|
550
592
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
551
593
|
updateHint(headerRow);
|
|
552
594
|
try {
|
|
553
|
-
|
|
595
|
+
// First, get raw data (headerRow=0) to show skipped rows
|
|
596
|
+
const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
597
|
+
headerRow: 0,
|
|
598
|
+
maxSheetSize: headerRow + 15
|
|
599
|
+
});
|
|
600
|
+
const rawSheet = Object.values(rawSheets)[0];
|
|
601
|
+
// Then, get parsed data with the chosen header row
|
|
602
|
+
const parsedSheets = await this._driver.streamFileMultiSheet(this._rawFileData.file, {
|
|
554
603
|
headerRow,
|
|
555
|
-
maxSheetSize: headerRow +
|
|
604
|
+
maxSheetSize: headerRow + 15
|
|
556
605
|
});
|
|
557
|
-
const
|
|
558
|
-
if (!
|
|
606
|
+
const parsedSheet = Object.values(parsedSheets)[0];
|
|
607
|
+
if (!rawSheet && !parsedSheet) {
|
|
559
608
|
if (previewDiv)
|
|
560
609
|
previewDiv.textContent = 'No data found';
|
|
561
610
|
return;
|
|
562
611
|
}
|
|
563
|
-
const dataRows = firstSheet.toRows().slice(0, 10);
|
|
564
|
-
const idxWidth = 6;
|
|
565
612
|
const colWidth = 22;
|
|
566
|
-
const
|
|
567
|
-
const
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
613
|
+
const idxWidth = 6;
|
|
614
|
+
const lines = [];
|
|
615
|
+
// Show skipped rows (rows before the header)
|
|
616
|
+
if (rawSheet && headerRow > 0) {
|
|
617
|
+
lines.push('');
|
|
618
|
+
lines.push(` ┌${'─'.repeat(100)}┐`);
|
|
619
|
+
lines.push(` │ ROWS 0-${headerRow - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
|
|
620
|
+
lines.push(` └${'─'.repeat(100)}┘`);
|
|
621
|
+
lines.push('');
|
|
622
|
+
const rawRows = rawSheet.toRows();
|
|
623
|
+
const rawCols = rawSheet.columns;
|
|
624
|
+
// Show raw header (row 0) - dimmed
|
|
625
|
+
lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
|
|
626
|
+
const skippedCount = Math.min(headerRow - 1, rawRows.length);
|
|
627
|
+
for (let i = 0; i < skippedCount; i++) {
|
|
628
|
+
const row = rawRows[i];
|
|
629
|
+
const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
|
|
630
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
631
|
+
lines.push(`${rowIdx}${cols} ← skipped`);
|
|
632
|
+
}
|
|
633
|
+
lines.push('');
|
|
634
|
+
lines.push(` ╔${'═'.repeat(100)}╗`);
|
|
635
|
+
lines.push(` ║ ▼ DATA STARTS HERE (Row ${headerRow} = Column Headers)`.padEnd(101) + '║');
|
|
636
|
+
lines.push(` ╚${'═'.repeat(100)}╝`);
|
|
637
|
+
lines.push('');
|
|
638
|
+
}
|
|
639
|
+
// Show header row and data rows from parsed result
|
|
640
|
+
if (parsedSheet) {
|
|
641
|
+
const headerLine = `${'HDR'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ◀ HEADERS`;
|
|
642
|
+
lines.push(headerLine);
|
|
643
|
+
lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * parsedSheet.columns.length, 120))}`);
|
|
644
|
+
const dataRows = parsedSheet.toRows().slice(0, 8);
|
|
645
|
+
dataRows.forEach((row, i) => {
|
|
646
|
+
const rowIdx = `[${headerRow + 1 + i}]`.padEnd(idxWidth);
|
|
647
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
648
|
+
lines.push(`${rowIdx}${cols}`);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
573
651
|
if (previewDiv) {
|
|
574
|
-
previewDiv.textContent =
|
|
652
|
+
previewDiv.textContent = lines.join('\n');
|
|
575
653
|
}
|
|
576
654
|
}
|
|
577
655
|
catch (err) {
|
|
@@ -678,9 +756,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
678
756
|
return;
|
|
679
757
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
680
758
|
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
759
|
+
const totalSkipped = headerRow + skipRows;
|
|
760
|
+
hintDiv.innerHTML = totalSkipped > 0
|
|
761
|
+
? `Row <strong>${headerRow + skipRows}</strong> will be used as column headers. ` +
|
|
762
|
+
`<strong>${totalSkipped}</strong> row${totalSkipped > 1 ? 's' : ''} above will be skipped.`
|
|
763
|
+
: `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
684
764
|
};
|
|
685
765
|
const updatePreview = () => {
|
|
686
766
|
if (!this._rawFileData?.text)
|
|
@@ -690,25 +770,59 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
690
770
|
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
691
771
|
updateHint();
|
|
692
772
|
try {
|
|
773
|
+
const colWidth = 22;
|
|
774
|
+
const idxWidth = 6;
|
|
775
|
+
const lines = [];
|
|
776
|
+
const totalOffset = headerRow + skipRows;
|
|
777
|
+
// Parse raw (no header offset) to show skipped rows
|
|
778
|
+
if (totalOffset > 0) {
|
|
779
|
+
const rawDf = this._driver.parseCSV(this._rawFileData.text, {
|
|
780
|
+
delimiter: delim,
|
|
781
|
+
headerRow: 0,
|
|
782
|
+
skipRows: 0,
|
|
783
|
+
hasHeader: true,
|
|
784
|
+
maxRows: totalOffset + 1
|
|
785
|
+
});
|
|
786
|
+
lines.push('');
|
|
787
|
+
lines.push(` ┌${'─'.repeat(100)}┐`);
|
|
788
|
+
lines.push(` │ ROWS 0-${totalOffset - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
|
|
789
|
+
lines.push(` └${'─'.repeat(100)}┘`);
|
|
790
|
+
lines.push('');
|
|
791
|
+
const rawCols = rawDf.columns;
|
|
792
|
+
lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
|
|
793
|
+
const rawRows = rawDf.toRows();
|
|
794
|
+
const skippedCount = Math.min(totalOffset - 1, rawRows.length);
|
|
795
|
+
for (let i = 0; i < skippedCount; i++) {
|
|
796
|
+
const row = rawRows[i];
|
|
797
|
+
const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
|
|
798
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
799
|
+
lines.push(`${rowIdx}${cols} ← skipped`);
|
|
800
|
+
}
|
|
801
|
+
lines.push('');
|
|
802
|
+
lines.push(` ╔${'═'.repeat(100)}╗`);
|
|
803
|
+
lines.push(` ║ ▼ DATA STARTS HERE (Row ${totalOffset} = Column Headers)`.padEnd(101) + '║');
|
|
804
|
+
lines.push(` ╚${'═'.repeat(100)}╝`);
|
|
805
|
+
lines.push('');
|
|
806
|
+
}
|
|
807
|
+
// Parse with actual settings for header + data rows
|
|
693
808
|
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
694
809
|
delimiter: delim,
|
|
695
810
|
headerRow,
|
|
696
811
|
skipRows,
|
|
697
812
|
hasHeader: true,
|
|
698
|
-
maxRows:
|
|
813
|
+
maxRows: 8
|
|
699
814
|
});
|
|
815
|
+
const headerLine = `${'HDR'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ◀ HEADERS`;
|
|
816
|
+
lines.push(headerLine);
|
|
817
|
+
lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * df.columns.length, 120))}`);
|
|
700
818
|
const dataRows = df.toRows();
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
const rowIdx = String(headerRow + skipRows + 1 + i).padEnd(idxWidth);
|
|
707
|
-
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
708
|
-
return `${rowIdx}${cols}`;
|
|
709
|
-
}).join('\n');
|
|
819
|
+
dataRows.forEach((row, i) => {
|
|
820
|
+
const rowIdx = `[${totalOffset + 1 + i}]`.padEnd(idxWidth);
|
|
821
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
822
|
+
lines.push(`${rowIdx}${cols}`);
|
|
823
|
+
});
|
|
710
824
|
if (previewDiv) {
|
|
711
|
-
previewDiv.textContent =
|
|
825
|
+
previewDiv.textContent = lines.join('\n');
|
|
712
826
|
}
|
|
713
827
|
}
|
|
714
828
|
catch (err) {
|
|
@@ -314,32 +314,75 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
314
314
|
const existingTable = wrapper.querySelector('.jux-table-wrapper');
|
|
315
315
|
if (existingTable) existingTable.remove();
|
|
316
316
|
|
|
317
|
+
const existingTabs = wrapper.querySelector('.jux-dataframe-tabs');
|
|
318
|
+
if (existingTabs) existingTabs.remove();
|
|
319
|
+
|
|
317
320
|
Object.entries(sheets).forEach(([name, df]) => {
|
|
318
321
|
this._sheets.set(name, df);
|
|
319
322
|
});
|
|
320
323
|
|
|
321
324
|
const sheetNames = Object.keys(sheets);
|
|
322
325
|
|
|
323
|
-
|
|
324
|
-
tabs: sheetNames.map(name => ({
|
|
325
|
-
id: name,
|
|
326
|
-
label: name,
|
|
327
|
-
content: ''
|
|
328
|
-
})),
|
|
329
|
-
activeTab: sheetNames[0]
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
this._tabs = tabs;
|
|
333
|
-
|
|
326
|
+
// Build custom tab bar with settings cogs
|
|
334
327
|
const tabsContainer = document.createElement('div');
|
|
335
328
|
tabsContainer.className = 'jux-dataframe-tabs';
|
|
336
|
-
wrapper.appendChild(tabsContainer);
|
|
337
329
|
|
|
338
|
-
|
|
330
|
+
const tabList = document.createElement('div');
|
|
331
|
+
tabList.className = 'jux-tabs-list';
|
|
332
|
+
|
|
333
|
+
sheetNames.forEach((sheetName, idx) => {
|
|
334
|
+
const tabBtn = document.createElement('button');
|
|
335
|
+
tabBtn.className = 'jux-tabs-button' + (idx === 0 ? ' jux-tabs-button-active' : '');
|
|
336
|
+
tabBtn.setAttribute('data-sheet', sheetName);
|
|
337
|
+
|
|
338
|
+
const labelSpan = document.createElement('span');
|
|
339
|
+
labelSpan.textContent = sheetName;
|
|
340
|
+
tabBtn.appendChild(labelSpan);
|
|
341
|
+
|
|
342
|
+
// Add settings cog icon
|
|
343
|
+
const cogBtn = document.createElement('button');
|
|
344
|
+
cogBtn.className = 'jux-dataframe-tab-settings';
|
|
345
|
+
cogBtn.title = 'Import settings for ' + sheetName;
|
|
346
|
+
cogBtn.innerHTML = `<span class="iconify" data-icon="lucide:settings" style="width:14px;height:14px;"></span>`;
|
|
347
|
+
cogBtn.addEventListener('click', (e) => {
|
|
348
|
+
e.stopPropagation();
|
|
349
|
+
this._showReshapeModal();
|
|
350
|
+
});
|
|
351
|
+
tabBtn.appendChild(cogBtn);
|
|
352
|
+
|
|
353
|
+
tabBtn.addEventListener('click', () => {
|
|
354
|
+
// Switch active tab
|
|
355
|
+
tabList.querySelectorAll('.jux-tabs-button').forEach(btn => {
|
|
356
|
+
btn.classList.remove('jux-tabs-button-active');
|
|
357
|
+
});
|
|
358
|
+
tabBtn.classList.add('jux-tabs-button-active');
|
|
359
|
+
|
|
360
|
+
// Show/hide panels
|
|
361
|
+
wrapper.querySelectorAll('.jux-tabs-panel').forEach(panel => {
|
|
362
|
+
(panel as HTMLElement).style.display = 'none';
|
|
363
|
+
});
|
|
364
|
+
const panel = document.getElementById(`${this._id}-panel-${sheetName}`);
|
|
365
|
+
if (panel) panel.style.display = 'block';
|
|
366
|
+
|
|
367
|
+
// Update current df reference
|
|
368
|
+
this._df = this._sheets.get(sheetName) || null;
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
tabList.appendChild(tabBtn);
|
|
372
|
+
});
|
|
339
373
|
|
|
340
|
-
|
|
374
|
+
tabsContainer.appendChild(tabList);
|
|
375
|
+
wrapper.appendChild(tabsContainer);
|
|
376
|
+
|
|
377
|
+
// Create panels for each sheet
|
|
378
|
+
sheetNames.forEach((sheetName, idx) => {
|
|
341
379
|
const df = sheets[sheetName];
|
|
342
380
|
|
|
381
|
+
const panel = document.createElement('div');
|
|
382
|
+
panel.className = 'jux-tabs-panel';
|
|
383
|
+
panel.id = `${this._id}-panel-${sheetName}`;
|
|
384
|
+
panel.style.display = idx === 0 ? 'block' : 'none';
|
|
385
|
+
|
|
343
386
|
const table = new Table(`${this._id}-table-${sheetName}`, {
|
|
344
387
|
striped: this._tableOptions.striped,
|
|
345
388
|
hoverable: this._tableOptions.hoverable,
|
|
@@ -352,10 +395,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
352
395
|
const columnDefs = df.columns.map(col => ({ key: col, label: col }));
|
|
353
396
|
table.columns(columnDefs).rows(df.toRows());
|
|
354
397
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
table.render(tabPanel);
|
|
398
|
+
wrapper.appendChild(panel);
|
|
399
|
+
table.render(panel);
|
|
359
400
|
|
|
360
401
|
if (this._tableOptions.filterable) {
|
|
361
402
|
const filterContainer = document.createElement('div');
|
|
@@ -388,13 +429,20 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
388
429
|
table.rows(filtered.toRows());
|
|
389
430
|
});
|
|
390
431
|
|
|
391
|
-
const tableWrapper =
|
|
432
|
+
const tableWrapper = panel.querySelector('.jux-table-wrapper');
|
|
392
433
|
if (tableWrapper) {
|
|
393
|
-
|
|
434
|
+
panel.insertBefore(filterContainer, tableWrapper);
|
|
394
435
|
}
|
|
395
436
|
}
|
|
396
437
|
});
|
|
397
438
|
|
|
439
|
+
// Ensure Iconify renders the cog icons
|
|
440
|
+
requestAnimationFrame(() => {
|
|
441
|
+
if ((window as any).Iconify) {
|
|
442
|
+
(window as any).Iconify.scan();
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
398
446
|
const totalRows = Object.values(sheets).reduce((sum, df) => sum + df.height, 0);
|
|
399
447
|
this._updateStatus(
|
|
400
448
|
`${sourceName} — ${sheetNames.length} sheets, ${totalRows} total rows`,
|
|
@@ -609,7 +657,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
609
657
|
</div>
|
|
610
658
|
<div class="jux-reshape-preview-container">
|
|
611
659
|
<div style="font-weight: 600; margin-bottom: 0.5rem; color: hsl(var(--foreground));">
|
|
612
|
-
Preview
|
|
660
|
+
Preview
|
|
613
661
|
</div>
|
|
614
662
|
<div id="${this._id}-preview" class="jux-reshape-preview"></div>
|
|
615
663
|
</div>
|
|
@@ -668,42 +716,85 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
668
716
|
|
|
669
717
|
const updateHint = (headerRow: number) => {
|
|
670
718
|
if (!hintDiv) return;
|
|
671
|
-
hintDiv.innerHTML =
|
|
672
|
-
`
|
|
719
|
+
hintDiv.innerHTML = headerRow > 0
|
|
720
|
+
? `Row <strong>${headerRow}</strong> will be used as column headers. ` +
|
|
721
|
+
`The <strong>${headerRow}</strong> row${headerRow > 1 ? 's' : ''} above will be skipped.`
|
|
722
|
+
: `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
673
723
|
};
|
|
674
724
|
|
|
675
725
|
const updatePreview = async () => {
|
|
676
726
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
677
|
-
|
|
678
727
|
updateHint(headerRow);
|
|
679
728
|
|
|
680
729
|
try {
|
|
681
|
-
|
|
730
|
+
// First, get raw data (headerRow=0) to show skipped rows
|
|
731
|
+
const rawSheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
|
|
732
|
+
headerRow: 0,
|
|
733
|
+
maxSheetSize: headerRow + 15
|
|
734
|
+
});
|
|
735
|
+
const rawSheet = Object.values(rawSheets)[0];
|
|
736
|
+
|
|
737
|
+
// Then, get parsed data with the chosen header row
|
|
738
|
+
const parsedSheets = await this._driver.streamFileMultiSheet(this._rawFileData!.file, {
|
|
682
739
|
headerRow,
|
|
683
|
-
maxSheetSize: headerRow +
|
|
740
|
+
maxSheetSize: headerRow + 15
|
|
684
741
|
});
|
|
742
|
+
const parsedSheet = Object.values(parsedSheets)[0];
|
|
685
743
|
|
|
686
|
-
|
|
687
|
-
if (!firstSheet) {
|
|
744
|
+
if (!rawSheet && !parsedSheet) {
|
|
688
745
|
if (previewDiv) previewDiv.textContent = 'No data found';
|
|
689
746
|
return;
|
|
690
747
|
}
|
|
691
748
|
|
|
692
|
-
const dataRows = firstSheet.toRows().slice(0, 10);
|
|
693
|
-
const idxWidth = 6;
|
|
694
749
|
const colWidth = 22;
|
|
750
|
+
const idxWidth = 6;
|
|
751
|
+
const lines: string[] = [];
|
|
752
|
+
|
|
753
|
+
// Show skipped rows (rows before the header)
|
|
754
|
+
if (rawSheet && headerRow > 0) {
|
|
755
|
+
lines.push('');
|
|
756
|
+
lines.push(` ┌${'─'.repeat(100)}┐`);
|
|
757
|
+
lines.push(` │ ROWS 0-${headerRow - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
|
|
758
|
+
lines.push(` └${'─'.repeat(100)}┘`);
|
|
759
|
+
lines.push('');
|
|
760
|
+
|
|
761
|
+
const rawRows = rawSheet.toRows();
|
|
762
|
+
const rawCols = rawSheet.columns;
|
|
763
|
+
|
|
764
|
+
// Show raw header (row 0) - dimmed
|
|
765
|
+
lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
|
|
766
|
+
|
|
767
|
+
const skippedCount = Math.min(headerRow - 1, rawRows.length);
|
|
768
|
+
for (let i = 0; i < skippedCount; i++) {
|
|
769
|
+
const row = rawRows[i];
|
|
770
|
+
const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
|
|
771
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
772
|
+
lines.push(`${rowIdx}${cols} ← skipped`);
|
|
773
|
+
}
|
|
695
774
|
|
|
696
|
-
|
|
697
|
-
|
|
775
|
+
lines.push('');
|
|
776
|
+
lines.push(` ╔${'═'.repeat(100)}╗`);
|
|
777
|
+
lines.push(` ║ ▼ DATA STARTS HERE (Row ${headerRow} = Column Headers)`.padEnd(101) + '║');
|
|
778
|
+
lines.push(` ╚${'═'.repeat(100)}╝`);
|
|
779
|
+
lines.push('');
|
|
780
|
+
}
|
|
698
781
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
782
|
+
// Show header row and data rows from parsed result
|
|
783
|
+
if (parsedSheet) {
|
|
784
|
+
const headerLine = `${'HDR'.padEnd(idxWidth)}${parsedSheet.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ◀ HEADERS`;
|
|
785
|
+
lines.push(headerLine);
|
|
786
|
+
lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * parsedSheet.columns.length, 120))}`);
|
|
787
|
+
|
|
788
|
+
const dataRows = parsedSheet.toRows().slice(0, 8);
|
|
789
|
+
dataRows.forEach((row, i) => {
|
|
790
|
+
const rowIdx = `[${headerRow + 1 + i}]`.padEnd(idxWidth);
|
|
791
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
792
|
+
lines.push(`${rowIdx}${cols}`);
|
|
793
|
+
});
|
|
794
|
+
}
|
|
704
795
|
|
|
705
796
|
if (previewDiv) {
|
|
706
|
-
previewDiv.textContent =
|
|
797
|
+
previewDiv.textContent = lines.join('\n');
|
|
707
798
|
}
|
|
708
799
|
} catch (err: any) {
|
|
709
800
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|
|
@@ -819,9 +910,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
819
910
|
if (!hintDiv) return;
|
|
820
911
|
const headerRow = parseInt(headerRowInput?.value) || 0;
|
|
821
912
|
const skipRows = parseInt(skipRowsInput?.value) || 0;
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
913
|
+
const totalSkipped = headerRow + skipRows;
|
|
914
|
+
hintDiv.innerHTML = totalSkipped > 0
|
|
915
|
+
? `Row <strong>${headerRow + skipRows}</strong> will be used as column headers. ` +
|
|
916
|
+
`<strong>${totalSkipped}</strong> row${totalSkipped > 1 ? 's' : ''} above will be skipped.`
|
|
917
|
+
: `Row <strong>0</strong> (first row) will be used as column headers.`;
|
|
825
918
|
};
|
|
826
919
|
|
|
827
920
|
const updatePreview = () => {
|
|
@@ -834,29 +927,68 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
834
927
|
updateHint();
|
|
835
928
|
|
|
836
929
|
try {
|
|
930
|
+
const colWidth = 22;
|
|
931
|
+
const idxWidth = 6;
|
|
932
|
+
const lines: string[] = [];
|
|
933
|
+
const totalOffset = headerRow + skipRows;
|
|
934
|
+
|
|
935
|
+
// Parse raw (no header offset) to show skipped rows
|
|
936
|
+
if (totalOffset > 0) {
|
|
937
|
+
const rawDf = this._driver.parseCSV(this._rawFileData.text, {
|
|
938
|
+
delimiter: delim,
|
|
939
|
+
headerRow: 0,
|
|
940
|
+
skipRows: 0,
|
|
941
|
+
hasHeader: true,
|
|
942
|
+
maxRows: totalOffset + 1
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
lines.push('');
|
|
946
|
+
lines.push(` ┌${'─'.repeat(100)}┐`);
|
|
947
|
+
lines.push(` │ ROWS 0-${totalOffset - 1} WILL BE SKIPPED (not imported)`.padEnd(101) + '│');
|
|
948
|
+
lines.push(` └${'─'.repeat(100)}┘`);
|
|
949
|
+
lines.push('');
|
|
950
|
+
|
|
951
|
+
const rawCols = rawDf.columns;
|
|
952
|
+
lines.push(`${'[0]'.padEnd(idxWidth)}${rawCols.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ← skipped`);
|
|
953
|
+
|
|
954
|
+
const rawRows = rawDf.toRows();
|
|
955
|
+
const skippedCount = Math.min(totalOffset - 1, rawRows.length);
|
|
956
|
+
for (let i = 0; i < skippedCount; i++) {
|
|
957
|
+
const row = rawRows[i];
|
|
958
|
+
const rowIdx = `[${i + 1}]`.padEnd(idxWidth);
|
|
959
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
960
|
+
lines.push(`${rowIdx}${cols} ← skipped`);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
lines.push('');
|
|
964
|
+
lines.push(` ╔${'═'.repeat(100)}╗`);
|
|
965
|
+
lines.push(` ║ ▼ DATA STARTS HERE (Row ${totalOffset} = Column Headers)`.padEnd(101) + '║');
|
|
966
|
+
lines.push(` ╚${'═'.repeat(100)}╝`);
|
|
967
|
+
lines.push('');
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// Parse with actual settings for header + data rows
|
|
837
971
|
const df = this._driver.parseCSV(this._rawFileData.text, {
|
|
838
972
|
delimiter: delim,
|
|
839
973
|
headerRow,
|
|
840
974
|
skipRows,
|
|
841
975
|
hasHeader: true,
|
|
842
|
-
maxRows:
|
|
976
|
+
maxRows: 8
|
|
843
977
|
});
|
|
844
978
|
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
const headerLine = 'Idx'.padEnd(idxWidth) + df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('| ');
|
|
850
|
-
const separator = '─'.repeat(Math.min(headerLine.length, 140));
|
|
979
|
+
const headerLine = `${'HDR'.padEnd(idxWidth)}${df.columns.map(c => String(c).substring(0, colWidth - 2).padEnd(colWidth)).join('│ ')} ◀ HEADERS`;
|
|
980
|
+
lines.push(headerLine);
|
|
981
|
+
lines.push(`${'───'.padEnd(idxWidth)}${'─'.repeat(Math.min(colWidth * df.columns.length, 120))}`);
|
|
851
982
|
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
983
|
+
const dataRows = df.toRows();
|
|
984
|
+
dataRows.forEach((row, i) => {
|
|
985
|
+
const rowIdx = `[${totalOffset + 1 + i}]`.padEnd(idxWidth);
|
|
986
|
+
const cols = Object.values(row).map(v => String(v ?? '').substring(0, colWidth - 2).padEnd(colWidth)).join('│ ');
|
|
987
|
+
lines.push(`${rowIdx}${cols}`);
|
|
988
|
+
});
|
|
857
989
|
|
|
858
990
|
if (previewDiv) {
|
|
859
|
-
previewDiv.textContent =
|
|
991
|
+
previewDiv.textContent = lines.join('\n');
|
|
860
992
|
}
|
|
861
993
|
} catch (err: any) {
|
|
862
994
|
if (previewDiv) previewDiv.textContent = `Error: ${err.message}`;
|
package/lib/styles/shadcn.css
CHANGED
|
@@ -589,49 +589,47 @@ code {
|
|
|
589
589
|
.jux-modal-position-fullscreen .jux-modal { width: 100%; max-width: 100%; height: 100vh; max-height: 100vh; border-radius: 0; }
|
|
590
590
|
|
|
591
591
|
/* ═══════════════════════════════════════════════════════════════════
|
|
592
|
-
*
|
|
592
|
+
* DATAFRAME TAB SETTINGS COG
|
|
593
593
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
594
594
|
|
|
595
|
-
.jux-
|
|
596
|
-
|
|
595
|
+
.jux-dataframe-tab-settings {
|
|
596
|
+
display: inline-flex;
|
|
597
|
+
align-items: center;
|
|
598
|
+
justify-content: center;
|
|
599
|
+
background: transparent;
|
|
600
|
+
border: none;
|
|
601
|
+
padding: 0.125rem;
|
|
602
|
+
margin-left: 0.375rem;
|
|
603
|
+
border-radius: 0.25rem;
|
|
604
|
+
cursor: pointer;
|
|
597
605
|
color: hsl(var(--muted-foreground));
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
background: hsl(var(--warning) / 0.08);
|
|
601
|
-
border-radius: var(--radius);
|
|
602
|
-
border: 1px solid hsl(var(--warning) / 0.2);
|
|
603
|
-
line-height: 1.5;
|
|
606
|
+
opacity: 0.5;
|
|
607
|
+
transition: all 0.15s;
|
|
604
608
|
}
|
|
605
609
|
|
|
606
|
-
.jux-
|
|
610
|
+
.jux-dataframe-tab-settings:hover {
|
|
611
|
+
opacity: 1;
|
|
612
|
+
background: hsl(var(--muted) / 0.5);
|
|
607
613
|
color: hsl(var(--foreground));
|
|
608
614
|
}
|
|
609
615
|
|
|
610
|
-
.jux-
|
|
611
|
-
|
|
612
|
-
border-radius: var(--radius);
|
|
613
|
-
padding: 1rem;
|
|
614
|
-
background: hsl(var(--muted) / 0.3);
|
|
615
|
-
max-height: 400px;
|
|
616
|
-
overflow: auto;
|
|
616
|
+
.jux-tabs-button-active .jux-dataframe-tab-settings {
|
|
617
|
+
opacity: 0.7;
|
|
617
618
|
}
|
|
618
619
|
|
|
619
|
-
.jux-
|
|
620
|
-
|
|
621
|
-
font-size: 0.6875rem;
|
|
622
|
-
white-space: pre;
|
|
623
|
-
color: hsl(var(--foreground));
|
|
624
|
-
line-height: 1.6;
|
|
625
|
-
tab-size: 4;
|
|
620
|
+
.jux-tabs-button-active .jux-dataframe-tab-settings:hover {
|
|
621
|
+
opacity: 1;
|
|
626
622
|
}
|
|
627
623
|
|
|
628
|
-
/*
|
|
629
|
-
|
|
630
|
-
|
|
624
|
+
/* Make tabs list scrollable if many sheets */
|
|
625
|
+
.jux-dataframe-tabs .jux-tabs-list {
|
|
626
|
+
overflow-x: auto;
|
|
627
|
+
flex-wrap: nowrap;
|
|
628
|
+
max-width: 100%;
|
|
629
|
+
}
|
|
631
630
|
|
|
632
|
-
.jux-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
border-radius: calc(var(--radius) - 2px);
|
|
631
|
+
.jux-dataframe-tabs .jux-tabs-button {
|
|
632
|
+
display: inline-flex;
|
|
633
|
+
align-items: center;
|
|
634
|
+
flex-shrink: 0;
|
|
637
635
|
}
|