forked from sheetjs/docs.sheetjs.com
refactor: replace tabs with `details` element, hide dropzone on file-uploads.
206 lines
5.7 KiB
JavaScript
206 lines
5.7 KiB
JavaScript
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 += `<details class="sheetjs-sheet-container">
|
|
<summary class="sheetjs-sheet-name">${sheetname}</summary>
|
|
<div class="sheetjs-tab-content">${table}</div>
|
|
</details>`;
|
|
});
|
|
}
|
|
|
|
// --- 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 `<div class="file-loaded">
|
|
<span class="file-name text-muted text-small">${fileName}</span>
|
|
<button type="button" id="unloadBtn" class="unload-btn">Unload</button>
|
|
</div>`;
|
|
};
|
|
|
|
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();
|