chore/docs: [Electron Demo] - improve code comments and documentation.
This commit is contained in:
parent
418f16a872
commit
8c4bd369c4
@ -52,8 +52,9 @@ The main process can run any NodeJS code, but it cannot access the DOM or any br
|
||||
|
||||
To allow communication between the main and renderer processes, Electron recommends building a [context bridge](https://www.electronjs.org/docs/latest/api/context-bridge) to expose low-level system calls and NodeJS APIs to the renderer process.
|
||||
|
||||
```js
|
||||
// preload.js
|
||||
Exposed APIs are available as `SheetJSDemoAPI` on the window object and proxied from the main process.
|
||||
|
||||
```js title="preload.js -- contextBridge API"
|
||||
const { contextBridge, ipcRenderer, shell } = require('electron');
|
||||
// import nodejs modules we wish to expose APIs from.
|
||||
const path = require('path');
|
||||
@ -95,7 +96,7 @@ For example, assuming a file input element on the page:
|
||||
|
||||
The event handler would process the event as if it were a web event:
|
||||
|
||||
```js
|
||||
```js title="index.js -- renderer process"
|
||||
async function handleFile(e) {
|
||||
const file = e.target.files[0];
|
||||
const data = await file.arrayBuffer();
|
||||
@ -121,7 +122,7 @@ For example, assuming a DIV on the page:
|
||||
|
||||
The event handler would process the event as if it were a web event:
|
||||
|
||||
```js
|
||||
```js title="index.js -- renderer process"
|
||||
async function handleDrop(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
@ -143,12 +144,11 @@ document.getElementById("drop").addEventListener("drop", handleDrop, false);
|
||||
|
||||
We can now use the exposed APIs from our preload script above to show the open dialog and try to parse the workbook from within the renderer process.
|
||||
|
||||
```js
|
||||
// index.js - renderer process
|
||||
|
||||
```js title="index.js -- renderer process"
|
||||
// our exposed bridge APIs are available as SheetJSDemoAPI on the window object
|
||||
const openFile = SheetJSDemoAPI.openFile; // request the open file dialog from the main process
|
||||
// We can also access the SheetJS package from the exposed bridge APIs
|
||||
// highlight-next-line
|
||||
const XLSX = SheetJSDemoAPI.xlsx;
|
||||
|
||||
/* this function will show the open dialog and try to parse the workbook */
|
||||
@ -164,10 +164,10 @@ async function importFile() {
|
||||
return XLSX.readFile(result.filePaths[0]);
|
||||
}
|
||||
```
|
||||
The xslx package here is being proxied from the main process via the bridge API.
|
||||
The actual implementation of the `openFile` function is handled within the main process in `main.js`.
|
||||
|
||||
```js
|
||||
// main.js - main process
|
||||
```js title="main.js -- main process"
|
||||
const { ipcMain, dialog } = require('electron');
|
||||
|
||||
ipcMain.handle('dialog:openFile', (_e, filters) =>
|
||||
@ -181,8 +181,7 @@ ipcMain.handle('dialog:openFile', (_e, filters) =>
|
||||
`showSaveDialog` shows a Save As dialog and returns the selected file name:
|
||||
|
||||
The implementation for saving files looks very similar to the one above thanks to our bridge API.
|
||||
```js
|
||||
// index.js - renderer process
|
||||
```js title="index.js -- renderer process"
|
||||
// our exposed bridge APIs are available as SheetJSDemoAPI on the window object
|
||||
const saveFile = window.SheetJSDemoAPI.saveFile; // request the save file dialog from the main process
|
||||
const XLSX = window.SheetJSDemoAPI.xlsx;
|
||||
@ -197,8 +196,7 @@ async function exportFile(workbook) {
|
||||
}
|
||||
```
|
||||
And here is the implementation of the `saveFile` function in `main.js`:
|
||||
```js
|
||||
// main.js - main process
|
||||
```js title="main.js -- main process"
|
||||
const { ipcMain, dialog } = require('electron');
|
||||
|
||||
ipcMain.handle('dialog:saveFile', (_e, filters) =>
|
||||
@ -206,6 +204,38 @@ ipcMain.handle('dialog:saveFile', (_e, filters) =>
|
||||
);
|
||||
```
|
||||
|
||||
### Working with OS level file open events.
|
||||
|
||||
The demo has been preconfigured to handle OS level file open events, such as the "open with" context menu or `open` CLI command for all file types SheetJS supports.
|
||||
In order to pre-register your application as a handler for any other file types, it is necessary to modify the `package.json` file as such.
|
||||
|
||||
```json
|
||||
// ...existing content
|
||||
"build": {
|
||||
"appId": "com.sheetjs.electron",
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": [ // supported extensions to register with the OS.
|
||||
"xls","xlsx","xlsm","xlsb","xml","csv","txt","dif",
|
||||
"sylk","slk","prn","ods","fods","htm","html","numbers"
|
||||
],
|
||||
"name": "Spreadsheet / Delimited File",
|
||||
"description": "Spreadsheets and delimited text files opened by SheetJS-Electron",
|
||||
"role": "Editor"
|
||||
}
|
||||
],
|
||||
"mac": { "target": "dmg" },
|
||||
"win": { "target": "nsis" },
|
||||
"linux": { "target": "deb" }
|
||||
},
|
||||
```
|
||||
this snippet makes it possible to generate installers for MacOS, Windows and Linux which will automatically register the application as a handler for the specified file types.
|
||||
|
||||
:::info pass
|
||||
|
||||
It is also possible to open files using the "open with" context menu without registering the application as a handler for the specified file types. This however, requires manually selecting the application binary as a target to open the file with.
|
||||
|
||||
:::
|
||||
## Complete Example
|
||||
|
||||
:::note Tested Deployments
|
||||
@ -215,8 +245,8 @@ This demo was tested in the following environments:
|
||||
| OS and Version | Architecture | Electron | Date |
|
||||
|:---------------|:-------------|:---------|:-----------|
|
||||
| macOS 15.3 | `darwin-x64` | `35.1.2` | 2025-03-31 |
|
||||
| macOS 14.5 | `darwin-arm` | `35.1.2` | 2025-03-30 |
|
||||
| Windows 11 | `win11-x64` | `33.2.1` | 2025-02-11 |
|
||||
| macOS 15.4 | `darwin-arm` | `36.1.0` | 2025-05-03 |
|
||||
| Windows 11 | `win11-x64` | `36.1.0` | 2025-05-03 |
|
||||
| Windows 11 | `win11-arm` | `33.2.1` | 2025-02-23 |
|
||||
| Linux (HoloOS) | `linux-x64` | `33.2.1` | 2025-01-02 |
|
||||
| Linux (Debian) | `linux-arm` | `33.2.1` | 2025-02-16 |
|
||||
@ -318,38 +348,6 @@ The program will run on ARM64 Windows.
|
||||
|
||||
:::
|
||||
|
||||
### Working with OS level file open events.
|
||||
|
||||
The demo has been preconfigured to handle OS level file open events, such as the "open with" context menu or `open` CLI command for all file types SheetJS supports.
|
||||
In order to pre-register your application as a handler for any other file types, it is necessary to modify the `package.json` file as such.
|
||||
|
||||
```json
|
||||
// ...existing content
|
||||
"build": {
|
||||
"appId": "com.sheetjs.electron",
|
||||
"fileAssociations": [
|
||||
{
|
||||
"ext": [ // supported extensions to register with the OS.
|
||||
"xls","xlsx","xlsm","xlsb","xml","csv","txt","dif",
|
||||
"sylk","slk","prn","ods","fods","htm","html","numbers"
|
||||
],
|
||||
"name": "Spreadsheet / Delimited File",
|
||||
"description": "Spreadsheets and delimited text files opened by SheetJS-Electron",
|
||||
"role": "Editor"
|
||||
}
|
||||
],
|
||||
"mac": { "target": "dmg" },
|
||||
"win": { "target": "nsis" },
|
||||
"linux": { "target": "deb" }
|
||||
},
|
||||
```
|
||||
this snippet makes it possible to generate installers for MacOS, Windows and Linux which will automatically register the application as a handler for the specified file types.
|
||||
|
||||
:::info pass
|
||||
|
||||
It is also possible to open files using the "open with" context menu without registering the application as a handler for the specified file types. This however, requires manually selecting the application binary as a target to open the file with.
|
||||
|
||||
:::
|
||||
### Testing
|
||||
|
||||
5) Download [the test file `pres.numbers`](https://docs.sheetjs.com/pres.numbers)
|
||||
@ -445,10 +443,10 @@ Electron 12 and later also require `worldSafeExecuteJavascript: true` and
|
||||
Electron 14 and later must use `@electron/remote` instead of `remote`. An
|
||||
`initialize` call is required to enable Developer Tools in the window.
|
||||
|
||||
:::
|
||||
|
||||
For demos built on top of Electron 36 and later we isolate the processes entirely and the demo no longer requires `@electron/remote`.
|
||||
However, `nodeIntegration: false` by default now means that the renderer process no longer has access to NodeJS APIs.
|
||||
To expose NodeJS APIs to the renderer process, we use the contextBridge API to expose APIs from the main process to the renderer process. [See more](https://www.electronjs.org/docs/latest/api/context-bridge). This has been best practice since Electron 25.
|
||||
:::
|
||||
|
||||
|
||||
[^1]: See ["Makers"](https://www.electronforge.io/config/makers) in the Electron Forge documentation. On Linux, the demo generates `rpm` and `deb` distributables. On Arch Linux and the Steam Deck, `sudo pacman -Syu rpm-tools dpkg fakeroot` installed required packages. On Debian and Ubuntu, `sudo apt-get install rpm` sufficed.
|
@ -50,14 +50,12 @@ async function exportWorkbookAsFile() {
|
||||
extensions: EXTENSIONS,
|
||||
},
|
||||
]);
|
||||
// -- 2. if canceled or no file path, return
|
||||
if (canceled || !filePath) return;
|
||||
// -- 3. write workbook to file
|
||||
// -- 2. write workbook to file
|
||||
try {
|
||||
XLSX.writeFile(currentWorkbook, filePath);
|
||||
window.SheetJSDemoAPI.message(`Exported to ${filePath}`);
|
||||
} catch (err) {
|
||||
// -- 4. if error, display error
|
||||
displayError(`Failed to export: ${err.message}`);
|
||||
}
|
||||
}
|
||||
@ -67,18 +65,19 @@ exportBtn.addEventListener("click", exportWorkbookAsFile);
|
||||
// Render workbook --> HTML tables
|
||||
// ---------------------------------------------------------------------------
|
||||
function renderWorkbookToTables(wb) {
|
||||
// -- 1. convert each sheet to HTML
|
||||
// -- 1. map through each sheet
|
||||
const html = wb.SheetNames.map((name) => {
|
||||
const sheet = wb.Sheets[name];
|
||||
// -- 2. convert sheet to HTML
|
||||
const table = XLSX.utils.sheet_to_html(sheet, { id: `${name}-tbl` });
|
||||
// -- 2. wrap in details element
|
||||
return `<details class="sheetjs-sheet-container">
|
||||
<summary class="sheetjs-sheet-name">${name}</summary>
|
||||
<div class="sheetjs-tab-content">${table}</div>
|
||||
</details>`;
|
||||
}).join(""); // -- 3. join into single string
|
||||
// -- 4. render to DOM
|
||||
}).join("");
|
||||
// CAUTION!: in production environments please sanitize the HTML output to prevent XSS attacks from maliciously crafted spreadsheets.
|
||||
htmlout.innerHTML = html; // single write → single re‑flow of the DOM
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -133,24 +132,21 @@ async function handleReadBtn() {
|
||||
extensions: EXTENSIONS,
|
||||
},
|
||||
]);
|
||||
// -- 2. if canceled or no file path, return
|
||||
if (canceled || !filePaths.length) return;
|
||||
// -- 3. if multiple files selected, return error
|
||||
if (filePaths.length !== 1)
|
||||
return displayError("Please choose a single file.");
|
||||
|
||||
showSpinner();
|
||||
await nextPaint(); // ensure spinner paints
|
||||
try {
|
||||
// -- 4. read the first selected file
|
||||
const filePath = filePaths[0];
|
||||
// -- 2. read the first selected file
|
||||
currentWorkbook = XLSX.readFile(filePath);
|
||||
renderWorkbookToTables(currentWorkbook);
|
||||
showLoadedFileUI(basename(filePath));
|
||||
} finally {
|
||||
hideSpinner();
|
||||
hideDropUI();
|
||||
// -- 5. reset error UI state
|
||||
onError && (onError.hidden = true);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user