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( "|" ); const dropContainer = document.getElementById("drop-container"); const dropzone = document.getElementById("drop"); const fileStatus = document.getElementById("fileStatus"); const exportBtn = document.getElementById("exportBtn"); const spinnerOverlay = document.getElementById("spinner-overlay"); const htmlout = document.getElementById("htmlout"); // open external links in default browser document.addEventListener("click", (e) => { if (e.target.tagName === "A" && e.target.href.startsWith("http")) { e.preventDefault(); electron.shell.openExternal(e.target.href); } }); /** * Export current HTML table as a spreadsheet file using Electron API. */ async function exportFile() { 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"], }); } exportBtn.addEventListener("click", exportFile, false); function renderWorkbookToTables(wb) { htmlout.innerHTML = ""; const sheetNames = wb.SheetNames; sheetNames.forEach((sheetName) => { const sheet = wb.Sheets[sheetName]; const sheetname = sheetName; const table = XLSX.utils.sheet_to_html(sheet); htmlout.innerHTML += `
${sheetname}
${table}
`; }); } // --- 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); showExportBtn(); } finally { hideSpinner(); hideDropUI(); } } // --- UI Templates and Helpers --- const getLoadedFileUI = (fileName) => { return `
${fileName}
`; }; const hideDropUI = () => { if (dropContainer) dropContainer.style.display = "none"; }; const showDropUI = () => { if (dropContainer) dropContainer.style.display = "block"; }; const hideLoadedFileUI = () => { if (fileStatus) fileStatus.innerHTML = ""; }; const hideOutputUI = () => { if (htmlout) htmlout.innerHTML = ""; }; const showLoadedFileUI = (fileName) => { const loadedFileUI = getLoadedFileUI(fileName); fileStatus.innerHTML = loadedFileUI; const unloadBtn = fileStatus.querySelector("#unloadBtn"); if (unloadBtn) { unloadBtn.addEventListener("click", () => { hideLoadedFileUI(); hideExportBtn(); showDropUI(); hideOutputUI(); }); } hideDropUI(); showExportBtn(); }; const hideExportBtn = () => { if (exportBtn) exportBtn.disabled = true; }; const showExportBtn = () => { if (exportBtn) exportBtn.disabled = false; }; function showSpinner() { if (spinnerOverlay) spinnerOverlay.style.display = "flex"; } function hideSpinner() { if (spinnerOverlay) spinnerOverlay.style.display = "none"; } // --- Event Listener Helpers --- /** * Add an event listener to an element if it exists. */ function addListener(id, event, handler) { const el = document.getElementById(id); if (el) el.addEventListener(event, handler, false); } /** * Attach drag-and-drop and file input listeners to the UI. */ function attachDropListeners() { addListener("readIn", "change", (e) => { showSpinner(); // Defer to next tick to ensure spinner renders before heavy work setTimeout(() => readFile(e.target.files), 0); }); addListener("readBtn", "click", handleReadBtn); if (!dropzone || !dropContainer) return; const handleDrag = (e) => { e.stopPropagation(); e.preventDefault(); e.dataTransfer.dropEffect = "copy"; dropContainer.classList.add("drag-over"); }; const handleDragLeave = (e) => { e.stopPropagation(); e.preventDefault(); dropContainer.classList.remove("drag-over"); }; dropzone.addEventListener( "drop", (e) => { e.stopPropagation(); e.preventDefault(); dropContainer.classList.remove("drag-over"); readFile(e.dataTransfer.files); }, false ); dropzone.addEventListener("dragenter", handleDrag, false); dropzone.addEventListener("dragover", handleDrag, false); dropzone.addEventListener("dragleave", handleDragLeave, false); dropzone.addEventListener("dragend", handleDragLeave, false); } // --- File Reader for Drag-and-Drop and Input --- /** * Read file(s) from input or drag-and-drop and render as table. */ async function readFile(files) { if (!files || files.length === 0) return; const f = files[0]; showSpinner(); try { const data = await f.arrayBuffer(); renderWorkbookToTables(XLSX.read(data)); showLoadedFileUI(f.name); } finally { hideSpinner(); } } // --- Initial Setup --- attachDropListeners();