Electron Adventures: Episode 6: Use D3 To Visualize Free Disk Space

In previous episode we created a small app that displays how much free disk space you have, in a very boring way.

Now it's time to spice it up! We won't touch the backend at all, and only modify the frontend.

Install D3

First we need to install D3.

$ npm install d3

Do not use CDN version. This will stop your app working offline, and even worse, it introduces potential security risk. It's possible to use CDNs safely, but Electron security already is much harder than web app security, so let's not overcomplicate things.

Most frontend apps these days use bundlers like rollup or webpack, but for now let's just include it as regular browser javascript.

So here's the frontend HTML code. I added some styling in advance:

<!DOCTYPE html>
<html>
  <body>
    <style>
      body { text-align: center; }
      #info { display: grid; grid-template-columns: 1fr 1fr 1fr; }
    </style>
    <h1>Free disk space</h1>
    <div id="info"></div>
    <script src="./node_modules/d3/dist/d3.js"></script>
    <script src="app.js"></script>
  </body>
</html>

A lot of frontend Javascript packages, even if they recommend using bundlers, have something you can include under node_modules/packagename/dist/packagename.js.

D3 app

And here's the D3 app. I won't explain it in too much detail, as D3 is a big subject, and it's only incidental to what we're doing here.

We get data from the backend thought displayFreeDiskSpace callback, then we loop it for each disk, and create div with an h3 header and a svg pie chart. Most of the complicated code is pie chart creation, and we use a lot of D3 helpers for it.

function displayFreeDiskSpace(data) {
  let info = d3.select("#info")

  for (let row of data) {
    let total = parseInt(row[1])
    let used = parseInt(row[2])
    let free = total - used
    let path = row[5]

    let data = { free, used }

    let div = info.append("div")
    div.append("h3").text(path)

    let svg = div
      .append("svg")
      .attr("width", "100px")
      .attr("height", "100px")
    let g = svg
      .append("g")
      .attr("transform", "translate(50,50)")

    let pie = d3.pie().value(d => d[1])
    let pieData = pie(Object.entries(data))
    let color = d3
      .scaleOrdinal()
      .domain(["used", "free"])
      .range(["red", "green"])

    g
      .selectAll("path")
      .data(pieData)
      .enter()
      .append("path")
      .attr("d", d3.arc()
        .innerRadius(0)
        .outerRadius(45)
      )
      .attr("fill", d => color(d.data[0]))
      .attr("stroke", "black")
      .style("stroke-width", "2px")
      .style("opacity", 0.7)
  }
}

Result

electron-adventures-06-screenshot.png

All the code for the episode is here.

In the next episode we'll have a fun little detour.