const XLSX = require('xlsx'); const electron = require('@electron/remote'); // --- Supported Extensions --- const EXTENSIONS = "xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html|numbers".split("|"); /** * Export current HTML table as a spreadsheet file using Electron API. */ async function exportFile() { const HTMLOUT = document.getElementById('htmlout'); const wb = XLSX.utils.table_to_book(HTMLOUT.getElementsByTagName("TABLE")[0]); const o = await electron.dialog.showSaveDialog({ title: 'Save file as', filters: [{ name: "Spreadsheets", extensions: EXTENSIONS }] }); XLSX.writeFile(wb, o.filePath); electron.dialog.showMessageBox({ message: "Exported data to " + o.filePath, buttons: ["OK"] }); } document.getElementById('exportBtn').addEventListener('click', exportFile, false); /** * Create a element for a table from a header row array. */ function createTableHead(headerRow) { const thead = document.createElement('thead'); const headRow = document.createElement('tr'); headerRow.forEach(cell => { const th = document.createElement('th'); th.textContent = cell !== undefined ? cell : ''; headRow.appendChild(th); }); thead.appendChild(headRow); return thead; } /** * Create a
element for a table from a 2D data array. */ function createTableBody(data) { const tbody = document.createElement('tbody'); for (let i = 1; i < data.length; ++i) { const row = data[i]; const tr = document.createElement('tr'); row.forEach(cell => { const td = document.createElement('td'); td.textContent = cell !== undefined ? cell : ''; tr.appendChild(td); }); tbody.appendChild(tr); } return tbody; } /** * Wrap a table in a responsive container div. */ function createResponsiveDiv(table) { const responsiveDiv = document.createElement('div'); responsiveDiv.className = 'table-responsive'; responsiveDiv.appendChild(table); return responsiveDiv; } // --- Main Table Processing --- /** * Render all sheets of a workbook as HTML tables with clickable tabs. */ function renderWorkbookToTables(wb) { const HTMLOUT = document.getElementById('htmlout'); const exportBtn = document.getElementById('exportBtn'); exportBtn.disabled = false; HTMLOUT.innerHTML = ""; // Create tab container const tabContainer = document.createElement('div'); tabContainer.className = 'sheetjs-tab-container'; // Create content container const contentContainer = document.createElement('div'); contentContainer.className = 'sheetjs-tab-content'; // Store tables for each sheet const tables = []; wb.SheetNames.forEach(function(sheetName, idx) { // Create tab button const tab = document.createElement('button'); tab.className = 'sheetjs-tab-btn text-small'; tab.textContent = sheetName; tab.setAttribute('data-sheet-idx', idx); if(idx === 0) tab.classList.add('active'); tab.addEventListener('click', function() { // Remove active from all tabs Array.from(tabContainer.children).forEach(btn => btn.classList.remove('active')); tab.classList.add('active'); // Hide all tables and show the selected one tables.forEach((tableDiv, tIdx) => { tableDiv.style.display = (tIdx === idx) ? '' : 'none'; }); }); tabContainer.appendChild(tab); // Create table for this sheet const sheet = wb.Sheets[sheetName]; const data = XLSX.utils.sheet_to_json(sheet, { header: 1 }); if (data.length === 0) { const emptyDiv = document.createElement('div'); emptyDiv.textContent = `Sheet '${sheetName}' is empty.`; tables.push(emptyDiv); contentContainer.appendChild(emptyDiv); return; } const table = document.createElement('table'); table.className = 'sheetjs-table'; // Remove caption, handled by tab table.appendChild(createTableHead(data[0])); table.appendChild(createTableBody(data)); const responsiveDiv = createResponsiveDiv(table); if(idx !== 0) responsiveDiv.style.display = 'none'; tables.push(responsiveDiv); contentContainer.appendChild(responsiveDiv); }); // Only show tabs if more than one sheet if (wb.SheetNames.length > 1) { HTMLOUT.appendChild(tabContainer); } HTMLOUT.appendChild(contentContainer); } // --- File Import Logic --- /** * Handle file selection dialog and render the selected spreadsheet. */ async function handleReadBtn() { const o = await electron.dialog.showOpenDialog({ title: 'Select a file', filters: [{ name: "Spreadsheets", extensions: EXTENSIONS }], properties: ['openFile'] }); if(o.filePaths.length == 0) throw new Error("No file was selected!"); showSpinner(); // yield to event loop to render spinner await new Promise(resolve => setTimeout(resolve, 200)); try { const filePath = o.filePaths[0]; const fileName = filePath.split(/[/\\]/).pop(); renderWorkbookToTables(XLSX.readFile(filePath)); showLoadedFileUI(fileName); } finally { hideSpinner(); } } // --- UI Templates and Helpers --- /** * Return HTML for the drag-and-drop area. */ function getDropAreaHTML() { return `Drag and drop a file here
or