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() |
Returns promise, ex:
|
|
information() info() |
Same as alert() |
|
confirm() |
|
|
error() |
Same as alert() |
|
bell()
|
Same as alert() . Produces a "beep".
|
|
wait() |
Does not return a promise, but a function you can close the wait dialog with :
|
|
input()
|
Return promise holding the form state, i.e { firstname: 'Arthur', age: 42 }
You can define a callback in options or DEFAULTS , to determine if the user can submit :
|
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 :
- Overflow content are always hidden
- Buttons or
.dialog-actions
are always placed near the bottom right
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.
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 / xsmall | 18vw / 15vh | xs-width / xs-height |
sm / small | 22vw / 18vh | sm-width / sm-height |
md / medium | 34vw / 31vh | md-width / md-height |
lg / large | 55vw / 52vh | lg-width / lg-height |
fullsize | 100vw / 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
}
state
is an object literal containing{ name: value }
for all inputs.dialog
is the dialog's actualHTMLDialogElement
, which can be used to manipulate input fields on the form (or anything else).
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 :
- GitHub, for github pages
- Github-pandoc.css, modified where needed
- Chris Ferdinandi, for the Gumshoe scroll spy
- highlight.js and contributors