DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Electron Adventures: Episode 88: Svelte CSV Viewer

I want to experiment with a few interesting OS integrations, but first let's get an app for that - a simple CSV viewer is Svelte.

When you start the app, it will open a file dialog and ask you to select one or more CSV files to open. Then it will open a new window for each selected file.

I picked a few CSV example files with Taylor Swift lyrics from Kaggle and put them into samples subfolder.

I'll be using d3-dsv to parse CSV files. It's part of the D3, but it can be used separately, so npm install d3-dsv.

index.js

First, we need to popup the dialog to select the CSV files.

let { app, BrowserWindow, dialog } = require("electron")

async function createWindow() {
  let { canceled, filePaths } = await dialog.showOpenDialog({
    properties: ["openFile", "multiSelections", "showHiddenFiles"],
    filters: [
      { name: "CSV files", extensions: ["csv"] },
      { name: "All Files", extensions: ["*"] }
    ],
    message: "Select a CSV file to open",
    defaultPath: `${__dirname}/samples`,
  })
  if (canceled) {
    app.quit()
  }
  for (let path of filePaths) {
    let qs = new URLSearchParams({ path }).toString();
    let win = new BrowserWindow({
      width: 1024,
      height: 768,
      webPreferences: {
        preload: `${__dirname}/preload.js`,
      },
    })
    win.loadURL(`http://localhost:5000/?${qs}`)
  }
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
Enter fullscreen mode Exit fullscreen mode

As I mentioned before, this is Electron API weirdness. File dialogs are really a frontend concern, so logically they should go to the frontend process, but Electron API moves them to the backend, so we have to jump through a few hoops here.

For a small change from the hex editor, we setup default filter to only show *.csv files, it can be overridden by the user through the OS-specific filter dropdown.

preload.js

let fs = require("fs")
let { contextBridge } = require("electron")

let q = new URLSearchParams(window.location.search)

let path = q.get("path")
let data = fs.readFileSync(path, "utf8")

contextBridge.exposeInMainWorld(
  "api", { path, data }
)
Enter fullscreen mode Exit fullscreen mode

A small change from the hex editor, we want to return a String, not a Buffer, so we assume CSV is in UTF8. If it's not, well, it's 21st century, you should have fixed that already.

src/App.svelte

And now for a very simple CSV viewer - we just dump everything into a simple HTML table, with no special handling of headers, or data types, or anything.

<script>
  import {csvParseRows} from "d3-dsv"

  let data = csvParseRows(window.api.data)
  let fileName = window.api.path.split("/").slice(-1)[0]
  let title = `CSV Viewer - ${fileName}`
</script>

<h1>CSV Viewer</h1>

<table>
  {#each data as row}
    <tr>
      {#each row as column}
        <td>{column}</td>
      {/each}
    </tr>
  {/each}
</table>


<svelte:head>
  <title>{title}</title>
</svelte:head>

<style>
:global(body) {
  margin: 0;
  background-color: #444;
  color: #fff;
}
tr {
  text-align: center;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Results

Here's the results:

Episode 88 Screenshot

Now that we have a base app, we'll be adding a few interesting enhancements to it over the next few episodes.

As usual, all the code for the episode is here.

Discussion (0)