electron-custom-dialog

Show customizable (HTML) dialog windows in your Electron app

Usage no npm install needed!

<script type="module">
  import electronCustomDialog from 'https://cdn.skypack.dev/electron-custom-dialog';
</script>

README

electron-custom-dialog

Electron-custom-dialog tries to simplify the process of creating and showing custom dialog windows in Electron.

Motivation

Dialog windows in Electron can be shown by using the dialog-module. These are native dialogs, whose appearance depends on the underlying windowing system. Electron apps often don't even try to look like native apps, so the graphical experience is a bit odd. Other approach is to create "fake" modal windows inside the application window in HTML. However, this approach will not work well on multi-window applications.

Features

  • Create dialog windows, which will look exactly as you wish
  • Open dialogs easily from both main and renderer processes
  • Easy-to-use promise-based API similar to electron.dialog
  • Works on Windows and Linux

Under the hood electron-custom-dialog creates Electron BrowserWindow-objects. A new BrowserWindow-object is created every time the dialog is opened. Opening dialogs from renderer-processes is implemented using IPC.

Installation

You can install electron-custom-dialog using npm or Yarn.

npm install electron-custom-dialog OR yarn install electron-custom-dialog

API

Electron-custom-dialog API has several funtions, which can be used either from main or renderer processes or from both.

Main-process functions

prepareDialog(options)

Prepare a custom dialog. Custom dialogs need to be prepared in the main-process before you can use them.

  • options Object
    • name String - Unique name for the dialog.
    • load(win, props) Function - Hook for loading the window content. Arguments passed to the load-function are:
      • win BrowserWindow - The BrowserWindow-object of the dialog. Can be used to customize the behaviour. You should call loadURL() or loadFile() for this argument.
      • props any - The props passed for the dialog. Use this, if you want to render the dialog using a template engine.
    • parent BrowserWindow | function - Set parent window for the dialog. Can also be a function returning a BrowserWindow-object.
    • windowOptions Object - Options for Electron BrowserWindow-constructor.

Returns a plain object with following properties:

  • open Function - The same as openDialog() but without the first argument name.

prepareDialogs(options)

Prepare several dialogs in one function call. Options can be given in an array or as separate arguments.

Main/renderer-process functions

openDialog(name, [props])

Opens a custom dialog which was initialized using prepareDialog(). Can be called from main or renderer process.

  • name String - Name of the dialog to open.
  • props Object (optional) - Props passed to the dialog renderer.

Returns a Promise, which is resolved with response data after the dialog is closed and rejected if there's an error opening the dialog.

Renderer-process functions

waitProps()

Returns a promise, which is resolved with props given to the openDialog() after they are available.

sendResponse(response)

  • response any serializable value - Response of the dialog.

Usage

Electron-custom-dialog can be used from both main and renderer processes. The dialogs should be prepared in the main process, after which they can be opened from anywhere in the app by using the name given during preparation. The following simple example gives you an idea how to use it:

dialog.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Simple dialog</title>
  </head>
  <body>
    <div id="root">
      <p id="question"></p>
      <button id="yesBtn">yes</button>
      <button id="noBtn">no</button>
    </div>
    <script>
      const {sendResponse, waitProps} = require('electron-custom-dialog')
      const questionEl = document.getElementById('question')
      const yesBtn = document.getElementById('yesBtn')
      const noBtn = document.getElementById('noBtn')

      waitProps().then((props) => {
        questionEl.textContent = props.question
        yesBtn.addEventListener('click', () => {
          sendResponse(true)
        });
        noBtn.addEventListener('click', () => {
          sendResponse(false)
        });
      })

    </script>
  </body>
</html>

main.js

const electron = require('electron')
const app = electron.app
const path = require('path')
const { prepareDialog, openDialog } = require('electron-custom-dialog')

app.on('activate', () => {
  prepareDialog({
  name: 'myDialog',
    load(win) {
      win.loadFile(path.join(__dirname, 'dialog.html'))
    }
  })
  const mainWindow = new BrowserWindow()
  mainWindow.loadFile(path.join(__dirname, 'main.html'))
})

main.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Main window</title>
  </head>
  <body>
    <div id="root">
    </div>
    <script>
      const {openDialog} = require('electron-custom-dialog')
      const rootEl = document.getElementById('root')
      openDialog('myDialog', {question: 'Are you sure?'}).then((result) => {
        rootEl.textContent = result ? 'Yeah!' : 'Nope.'
      })
    </script>
  </body>
</html>

See code in example/ and test/ for more details.

Known issues

The dialog is shown without content for a while before content gets loaded

Dialog windows should be used for acquiring immediate user input. It's important that the window is shown immediately when the need for user input arises, and that's why dialog windows are created with show-property set to true. Creating dialogs hidden and using show/hide would be another alternative to work around this problem, but there are some slight issues with it:

Not tested on MacOS

Electron-custom-dialog has been tested on Windows 10 and Kubuntu. MacOS has not been tried but might work.