SimplyDialogs

This is demonstration / test page for github.com/davidkonrad/simplydialogs. Latest release v1.0.11 (17.04.2024).

Simplydialogs offers a range of common dialogs, all highly customizeable. Comes with no dependencies and a really small footprint.

Summary

Type Sample Remarks
alert() alert() sample
Dlg.alert('Lorem ipsum ...')
Dlg.alert('Lorem ipsum ...', options)
Returns promise, ex:
Dlg.alert('Lorem ipsum ...').then(answer => { 
  console.log(answer) //true except when cancelled (ESC)
})
information()
info()
info() sample Same as alert()
confirm() confirm() sample
Dlg.confirm('Lorem ipsum ...').then(answer => { 
  console.log(answer) //true or false
})
error() error() sample Same as alert()
bell() bell() sample Same as alert(). Produces a "beep".
wait() wait() sample Does not return a promise, but a function you can close the wait dialog with :
const wait = Dlg.wait('Lorem ipsum ...')
//do something
wait.close()
input() input() sample Return promise holding the form state, i.e { firstname: 'Arthur', age: 42 }
Dlg.input('Lorem ipsum ...', options).then(state) => {
 ... 
})
You can define a callback in options or DEFAULTS, to determine if the user can submit :
callback: function(state, dialog) { 
  return state.firstname !== '' 
})

Basic usage

Dlg.alert('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(answer => {	
  console.log('alert', answer) 
})

Dlg.confirm('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(answer => {…})
Dlg.information('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(answer => {…})
Dlg.bell('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(answer => {…})
Dlg.error('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(answer => {…})

const wait = Dlg.wait('Lorem ipsum dolor sit amet, consectetur adipiscing elit')
setTimeout(() => { wait.close() }, 1500)

Dlg.input('Lorem ipsum dolor sit amet, consectetur adipiscing elit').then(input => {
  console.log('input', input)
})

Long text samples:

Stackable

SimplyDialogs can be stacked upon each other endlessly :

(with forced delay effect).

(with forced right bottom positioning)

Global settings

DEFAULTS

Simplydialogs uses internally a defaults literal, which is accessible through the .DEFAULTS property :

let defaults = {
  enterSubmit: true,
  escape: true,
  backdrop: undefined,
  classes: '',
  headers: {
    alert: 'Alert', 
    error: 'Error',
    confirm: 'Confirm',
    information: 'Information',
    bell: 'Notice',
    input: ''
  },
  icons: {
    alert: '⚠', 
    error: '⛔',
    confirm: '✔️',
    information: '💡',
    bell: '🔔',
    wait: '⚙️',
    input: '✏️'
  },
  buttons: {
    captions: {
      ok: 'Ok',
      cancel: 'Cancel',
      yes: 'Yes',
      no: 'No'
    },
    classes: {
      ok: '',
      cancel: '',
      yes: '',
      no: ''
    }
  },
  input: {
    formLayout: 'left full-width',
    classes: {
      label: '',
      input: ''
    },
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input ', name: 'input', placeholder: 'Enter text ..' },
    ],
    callback: function(state) {
      return state.input && state.input.length > 1
    },
    promise: undefined
  }
}

You can alter those defaults by simple assignment, i.e <instance>.DEFAULTS.icons.alert = '<img src="path/to/image">' . Changes to DEFAULTS are global, but you can still pass alternative options to each dialog (see later).

Captions, language-settings

There is no such thing as "language" in SimplyDialogs, but most people would like to adjust their basic dialogs to the lingo or language they use. You can use DEFAULTS to change the dialogs into "spanish" as default by :

const Dlg = SimplyDialogs
Dlg.DEFAULTS.buttons.captions = {
  ok: 'Ok',	
  yes: 'Aceptar',
  no: 'Salir',
  cancel: 'Cancelar'
}
Dlg.DEFAULTS.headers = {
  alert: 'Alerta', 
  error: 'Error',
  confirm: 'Confirmar',
  information: 'Información',
  bell: 'Nota',
  input: 'Input'
}

If a button.caption is '', undefined or similar, the button will be hidden.

Icons

Icons can be replaced the same way; either individually or as a whole. For example, use another standard unicode emoji :

Dlg.DEFAULTS.icons.alert = '💥' //U+1F4A5, collision

See also the integration section. Here an example using Font Awesome icons :

Dlg.DEFAULTS.icons = {
  alert: '<i class="fa fa-warning" style="color:fuchsia;"></i>',
  information: '<i class="fa fa-info-circle" style="color:royalblue;"></i>',
  confirm: '<i class="fa fa-question-circle" style="color:forestgreen;"></i>',
  bell: '<i class="fa fa-bell" style="color:orange;"></i>',
  error: '<i class="fa fa-times-circle-o" style="color:maroon;"></i>',
  input: '<i class="fa fa-pencil" style="color:navy;"></i>',
  wait: '<i class="fa fa-gear" style="color:gray;"></i>'
}

::backdrop

Across the browsers, the default ::backdrop more or less look like this :

dialog::backdrop {
  position: fixed;
  top: 0px;
  right: 0px;
  bottom: 0px;
  left: 0px;
  background: rgba(0, 0, 0, 0.1);
}

You can change DEFAULTS.backdrop in order to change the background of the dialogs. You can specify the background style itself by passing a valid CSS class-definition. Here are some examples:

Dlg.DEFAULTS.backdrop = 'background: rgba(0, 0, 0, 0);'
Dlg.DEFAULTS.backdrop = 'background: rgba(0, 0, 0, 0.5);'

Or get more inspiration at https://projects.verou.me/css3patterns/, where the ideas below are taken from :

Dlg.DEFAULTS.backdrop = `background: linear-gradient(27deg, #151515 5px, transparent 5px) 0 5px,
  linear-gradient(207deg, #151515 5px, transparent 5px) 10px 0px,
  linear-gradient(27deg, #222 5px, transparent 5px) 0px 10px,
  linear-gradient(207deg, #222 5px, transparent 5px) 10px 5px,
  linear-gradient(90deg, #1b1b1b 10px, transparent 10px),
  linear-gradient(#1d1d1d 25%, #1a1a1a 25%, #1a1a1a 50%, transparent 50%, transparent 75%, #242424 75%, #242424);
  background-color: #131313;
  opacity: 0.4;
  background-size: 20px 20px;`

Dlg.DEFAULTS.backdrop = `background: linear-gradient(63deg, #999 23%, transparent 23%) 7px 0,
  linear-gradient(63deg, transparent 74%, #999 78%),
  linear-gradient(63deg, transparent 34%, #999 38%, #999 58%, transparent 62%), #444;
  opacity: 0.4;
  background-size: 16px 48px;`

.classes

You can specify default classes for the <dialog element itself. Either static through .DEFAULTS or on the fly via options :

Dlg.DEFAULTS.classes = 'my dialog classes'

SimplyDialogs have some predefined classes you can use to control the size and the position of the dialog :

Sizing

You can show static sized dialogs by using the sizing classes : xs,xsmall,sm,small, md,medium,lg,large :

Dlg.DEFAULTS.classes = 'small'

Will force any dialog into type small. Using explicit sizing classes have two advantages :

If you are not sure about the size of the content, but sure about how large the dialog should be, you can use *-height and *-width classes.

Dlg.DEFAULTS.classes = 'md-width'

All dialogs will have the width of a md dialog, but growth in height according to the content.

Positioning

Per default all dialogs are shown in the center of the viewscreen, and vertically centerered between middle and top. You can force the position by using the top, bottom, middle, left, right, center classes, in any combination.

buttons.classes

You can specify default classes for each button, your own layout or adopting the style of a framework.

Dlg.DEFAULTS.buttons.classes = {
  ok: 'my btn classes',
  cancel: '',
  yes: '',
  no: ''
}

Options

Any of the DEFAULTS settings can be overruled by passing an options literal as second parameter. The structure of the options argument should follow the same format as DEFAULTS :

Dlg.alert('Lorem ipsum dolor sit amet, consectetur adipiscing elit', { icons: { alert: null }})

enterSubmit

enterSubmit allows the dialog to be submitted on keyboard enter. You can turn enterSubmit on and off for all type of dialogs (except wait) but is mostly useful for the input()-dialog. Note: If set to true you can still enter returns / line breaks in textareas, but anywhere else it will submit the dialog (i.e the form).

const options = {
  enterSubmit: (true | false),
  input: {
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input', name: 'input', spellcheck: false },
      {	type: 'textarea', label: 'Textarea', name: 'textarea', spellcheck: false }
    ],
    callback: function(state) {
      return state.input.length > 2
    }
  }
}

This produces a form where you need to enter at least 3 characters to the <input> field, and also have an optional <textarea>. In the first sample you can hit enter and submit the form, in the second you'll need to click on OK.

escape

Use the escape-option to manage hits on ESC. If false the user cannot cancel the dialog by presseing ESC. The default value is true, except for the wait-dialog, which always overrules both enterSubmit and escape.

As example, the same dialog as above for enterSubmit. Default settings, ESC turned off and both ESC and enter turned off, so the user need to click on either OK or cancel.

Sizing on the fly

Use the classes option to set a static size on the dialog. The builtin classes are :

xs / xsmall18vw / 15vhxs-width / xs-height
sm / small22vw / 18vhsm-width / sm-height
md / medium34vw / 31vhmd-width / md-height
lg / large55vw / 52vhlg-width / lg-height
fullsize100vw / 100vh

Example, show a xsmall sized information-dialog :

Dlg.info('some text ...', { classes: 'xs' }).then(..)

Note: The examples show dimensions, they are not meant to fit

Complex options

As reference, the layout of the options-literal look like this:

{
  enterSubmit: ,
  escape: ,
  backdrop: ,
  classes: ,
  headers: {},
  icons: {},
  buttons: {
    captions: {},
    classes: {},
  },
  input: {
    formLayout: ,
    classes: { label: , input: , }
    inputs: [],
    callback: ,
    promise: ,
  }
}

As example, define the following CSS-class and pass the literal :

dialog.nasa-earth {
  background: url(https://explorer1.jpl.nasa.gov/assets/images/galleries/1972_BlueMarble_115334main_image_feature_329_ys_full.jpg);
  background-size: cover;
  color: yellow;
  min-height: 50vh;
  font-size: x-large;
}
const options = {
  classes: 'nasa-earth lg-height sm-width',
  icons: { confirm: '👽' },
  headers: { confirm: null },
  buttons: { captions: { yes: 'No', no: 'Defently not!' }}
}
Dlg.confirm('Are There Any Chance of Intelligent Life in The Universe, anywhere?', options)

;- it is hard to figure out examples, or maybe I am just stoned.

Image Viewer

Reset button-captions and the header :

const msg = '<img src="https://explorer1.jpl.nasa.gov/assets/images/galleries/1972_BlueMarble_115334main_image_feature_329_ys_full.jpg" style="max-height:75vh;">'
const options = {
  headers: { bell: '' },
  icons: { bell: '' },
  buttons: { captions: { ok: null }}
}
Dlg.bell(msg, options)

Maps example

You can use HTML in headers, buttons and the dialog message itself. Here the informtion() dialog showing a Google Maps destination :

const msg = 'The Great Pyramid of Giza is the largest Egyptian pyramid and tomb of<br> Fourth Dynasty pharaoh Khufu. Built in the 26th century BC during a<br> period of around 27 years. '
const html = '<div class="mapouter"><div class="gmap_canvas"><iframe width="600" height="500" id="gmap_canvas" src="https://maps.google.com/maps?q=the%20great%20pyramids&t=k&z=13&ie=UTF8&iwloc=&output=embed" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe><a href="https://www.whatismyip-address.com"></a><br><style>.mapouter{position:relative;text-align:right;height:500px;width:600px;}</style><a href="https://www.embedgooglemap.net">embedgooglemap.net</a><style>.gmap_canvas {overflow:hidden;background:none!important;height:500px;width:600px;}</style></div></div>'
const options = {
  headers: { information: 'The Great Pyramid of Giza' }
}
Dlg.information(msg + html, options)

Additional styling

SimplyDialogs comes with diminutive styling - the very basics like position, max-width and so on - and you are supposed to want to change that styling. There are two ways :

1. Override defaults

Simply override the few CSS3 rules found in dialogs.css (must be declared after) :

dialog {
  border-radius: 0;
}
dialog h4 {
  font-size: medium;
}

etc. That will obvious inflict on all dialogs.

2. Attach a named CSS class to .classes

More suitable if you want to style a certain dialog differently. Here is an example with a kind of "Merry X-mas dialog", that overrule many of the default styles :

dialog.x-mas {
  border-radius: 0;
  border: 1mm ridge #34a65f !important;
  background: linear-gradient(45deg, #bf953f, #fcf6ba, #b38728, #fbf5b7, #aa771c);
}
dialog.x-mas h4 {
  font-size: xxx-large;
  font-family: 'Mountains of Christmas';
  font-weight: 900;
  color: #cc231e;
  text-shadow: 1px 1px #34A65F;
}
dialog.x-mas .dialog-message {
  font-size: medium;
  font-family: cursive;
  color: #cc231e;
  text-shadow: -1px -1px gold;
}
dialog.x-mas button {
  background-color: #0f8a5f;
  font-size: x-large;
  font-weight: bold;
  padding: 7px;
  color: gold;
  border-color: gold;
  padding-left: 20px;
  padding-right: 20px;
}

Then activate the dialog in code like this :

const options = {
  classes: 'x-mas',
  headers: { alert: 'Merry Christmas ...' },
  icons: { alert : '🎄' },
  buttons: { captions: { ok: '✨ Ok ✨' }}
}
const msg = '🎀 🎁 Frohe Weihnachten, メリークリスマス, Joyeux Noël, Feliz Navidad, 聖誕快樂, Buon Natale, Glædelig Jul ...!'
Dlg.alert(msg, options)

wait

When using the wait-dialog, it can be convenient to update the dialog for details about progress or whatever there is going on. wait has two special functions for updating the msessage, setText and addText. Both takes a string as argument, that can be plain text or HTML.

setText

setText replaces the message with new content. Consider this mockup :

let counter = 0
let interval = undefined
const wait = Dlg.wait('Lorem ipsum ...')
const replace = function() {
  counter ++
  wait.setText(`text or message changed ${counter} time(s)`)
  if (counter > 5) {
    clearInterval(interval)
    wait.close()
  }
}
interval = setInterval(replace, 1000)

addText

addText appends something to the already existing text/message. The content can be plain text or HTML. Consider this mockup :

let counter = 0
let interval = undefined
const wait = Dlg.wait('Lorem ipsum ...')
const append = function() {
  counter ++
  wait.addText(`<br>${counter} line(s) added to message`)
  if (counter > 5) {
    clearInterval(interval)
    wait.close()
  }
}
interval = setInterval(append, 1000)

Advanced inputs

Passing options literal

You can alter the input form either by changing DEFAULTS or by passing an options literal to input(msg, options) (what you should do) :

const options = {
  input: { 
    formLayout: '',
    classes: {
      label: '',
      input: ''
    },
    inputs: [ {…}, ... ],
    callback: undefined
  }
}

Just pass the attributes you need, if not set .DEFAULTS are used. The elements in the form is positioned by a CSS grid, via .formLayout top|left you can specify the label positions.

Define inputs

inputs is an array of objects defining each input for the form :

const options = {
  input: {
    inputs: [
      { type: 'input', inputType: 'text', label: 'Input', name: 'input', placeholder: 'Input required' },
      { type: 'textarea', label: 'Textarea', name: 'textarea', placeholder: 'Additional text', rows: 4 }
    ]
  }
}
Dlg.input('Lorem ipsum dolor sit amet, consectetur adipiscing elit', options)

.formLayout

formLayout can contain left/top and full-width. The basic layout have huge importance of the look of the dialog. Here all the combinations along with the input/textarea example above :

const options = {
  input: {
    formLayout: (not set | top | top full-width | left | left full-width | '')
    inputs: [
      ...
    ]
  }
}

.inputType

For <input>'s you specify the type throgh inputType. If the browser support the input type it can be shown. Pass special properties by their name, i.e value, min, placeholder :

const options = {
  input: {
    inputs: [
      { type: 'input', inputType: 'text', label: 'Text', name: 'input', spellcheck: false },
      { type: 'input', inputType: 'checkbox', label: 'Checkbox', name: 'checkbox', checked: true },
      { type: 'input', inputType: 'color', label: 'Color', name: 'color', value: '#123456' },
      { type: 'input', inputType: 'number', label: 'Number', name: 'number', value: 42 },
      { type: 'input', inputType: 'password', label: 'Password', name: 'password', style: 'color:maroon;' },
      { type: 'input', inputType: 'date', label: 'Date', name: 'date' },
      { type: 'input', inputType: 'file', label: 'File', name: 'file' },
      { type: 'input', inputType: 'url', label: 'Url', name: 'url' },
      { type: 'input', inputType: 'range', label: 'Range', name: 'range', value: 25, max: 100 },
      { type: 'input', inputType: 'hidden', label: '', name: 'hidden', value: 'You can pass extra values to the form via hidden inputs' }
    ]
  }
}
Dlg.input('Lorem ipsum dolor sit amet, consectetur adipiscing elit', options).then(function(input) {
  console.log('result', input)
})

Radios and selects

Radios are "paired" inputs and selects have child options, so those need a extended definition. Notice the use of value :

const options = { 
  input: { 
    inputs [
      { type: 'radio', label: 'radio', name: 'radio', value: 'option2',
          options: [
            { label: 'option1', value: 'option1' },
            { label: 'option2', value: 'option2' },
            { label: 'option3', value: 'option3' }
          ]
      },
      { type: 'select', label: 'select', name: 'select', value: 'option3',
          options: [
            { label: 'option1', value: 'option1' },
            { label: 'option2', value: 'option2' },
            { label: 'option3', value: 'option3' }
          ]
      }
    ],
    callback: function(state) {
      return state.radio !== null && state.select !== null
    }
  }
}	

Form validation

There are two ways to validate form input: input.callback and input.promise.

If (and only if) you specify a callback (or a promise), the submit-button is disabled and the callback will determine when it should be enabled. Callbacks are triggered whenever any of the inputs changes state, and the result will be handled as a boolean; i.e if you return true the submit-button will be enabled. The callback passes two params :

callback: function(state, dialog) {
  return true || false
}

callback

Example: You are not allowed to submit before you have entered the secret number 47 :

const options = {
  headers: { input: 'What is 42 with inflation?' },
  icons: { input: '🖖' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'password', label: '', name: 'star-trek-number' }
    ],
    callback: function(state) {
      return parseInt(state['star-trek-number']) === 47
    }
  }
}
Dlg.input(null, options).then(input => {
  console.log(input)
})

promise

A promise should be used when you have more complex / time consuming validation schemes. For example, if you need to lookup an username in your backend, to test if it is already in use. In the below we just mimick a server-side call by using a timeout :

const options = {
  headers: { input: 'Enter user name' },
  icons: { input: '' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'text', label: '', name: 'username' }
    ],
    promise: function(state) {
      return new Promise(function(resolve) {
        if (state.username.length < 5) return resolve(false)
        const letters = /^[0-9a-zA-Z]+$/
        if (!letters.test(state.username)) return resolve(false)
        setTimeout(function() {
          resolve(true)
        }, 1000)
      })
    }
  }
}
Dlg.input('Must be at least 5 characters long and may only contain letters and numbers', options).then(input => {
  console.log(input)
})

Login Dialog

Here is an example of how to show a modal login-dialog. Notice the use of autocomplete: off which turns off annoying auto suggestions :

const options = {
  headers: { input: 'Login' },
  icons: { input: '🔑' },
  input: {
    inputs: [	
      { type: 'input', inputType: 'text', label: 'Username', name: 'username', autocomplete: 'off' },
      { type: 'input', inputType: 'password', label: 'Password', name: 'password' }  
    ],
    callback: function(state) {
      return state.username.length > 0 && state.password.length > 0
    }
  }
}
Dlg.input('', options).then(input => {
  console.log('login', input)
  // { username: '...', password: '...' }
})

File input with preview

You kan combine a image type input with a file type input, and by that create an image-upload-dialog with preview. It takes a little more options-tweaks than the prior examples:

const options = {
  headers: { input: 'Upload Image' },
  icons: { input: '🖼' },
  input: {
    inputs: [
      { type: 'input', 
        inputType: 'image', 
        label: 'Preview', 
        name: 'preview', 
        alt: 'No image yet', 
        disabled: 'disabled',
        src: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"/>',
        style: 'min-height: 30vh; max-height: 50vh; border: 1px solid #dadada; object-fit: contain;' 
      }, { 
        type: 'input', 
        inputType: 'file', 
        label: 'Image', 
        name: 'file', 
        accept: 'image/png, image/gif, image/jpeg' 
      }
    ],
    callback: function(state, dialog) {
      if (state.file) {
        dialog.querySelector('input[type="image"]').src = URL.createObjectURL(state.file)
        return true
      }
    }
  }
}
Dlg.input('Select image file <br><sup>(PNG, GIF, JPG supported)</sup>', options).then(function(input) {
  console.log('image upload', input)
})

Integration

Example, Bootstrap 5

Example, Materialize CSS

Credits

This site was easy to produce, thanks to :