@marccawood/lit-dialogs
v0.1.0
Published
Promise-returning modal dialogs as themeable Lit web components.
Downloads
19
Maintainers
Readme
@marccawood/lit-dialogs
Promise-returning modal dialogs as Lit web components. alert, confirm,
prompt and choice, plus the shared chrome to build your own dialogs on.
Depends only on lit. No build tool assumptions, no CSS framework, no icons.
Live example → — cd example && npm install && npm run dev
For non-modal notifications see the sibling package
@marccawood/lit-toast.
Install
npm install @marccawood/lit-dialogs litUse
import { HostDialogs, defineHostDialogs } from '@marccawood/lit-dialogs';
defineHostDialogs();Render one element in your app shell:
<host-dialogs></host-dialogs>Then call it from anywhere through the singleton:
await HostDialogs.instance?.alert('Saved.');
const ok = await HostDialogs.instance?.confirm('Delete this table?');
const name = await HostDialogs.instance?.prompt('New name', 'Untitled');
const pick = await HostDialogs.instance?.choice('Export as', ['CSV', 'JSON', 'SQL']);confirm is choice with Yes/No. Cancel, Escape, and the close-X all resolve
null — undefined for alert.
defineHostDialogs(tag) takes an optional tag name and skips an
already-registered tag, so importing the module twice is safe.
Why promises
A dialog waits for a person. The native window.confirm blocks the main thread
to return its boolean, which a custom element cannot do, so the answer has to
arrive later. A promise keeps the call and its consequence on one line:
if (await dialogs.confirm('Delete this table?')) await store.tables.remove(id);It also makes the queue work. Only one dialog shows at a time, so three calls
with no await between them open one after the other instead of fighting over
the single <dialog> element.
Build your own dialogs on the same chrome
import { css, html, LitElement } from 'lit';
import { ctrlEnterSubmits, dialogChromeStyles, makeDialogDraggable } from '@marccawood/lit-dialogs';
class MyDialog extends LitElement {
static styles = [
dialogChromeStyles,
css`
dialog {
min-width: 420px;
}
`,
];
// …
}The expected markup is a <dialog> with a .close-x button, a <form>, a
.dialog-header (title + .header-actions) and a .dialog-body. The full
shape is documented at the top of src/dialog-chrome.ts,
and example/src/custom-dialog.ts is a
working one.
ctrlEnterSubmits— wire as@keydownon the<dialog>; Ctrl+Enter and Cmd+Enter submit the form from any field inside.makeDialogDraggable(dialog, header)— a native<dialog>centers itself and cannot be moved; this makes the header a drag handle. Idempotent.
Dialogs go full-screen below 640px viewport width.
Theme
Set these on any ancestor. Every one falls back to the value shown, so a project that sets nothing still looks finished.
| Token | Fallback |
| --------------------------------------------------------- | --------------------------------------- |
| --dlg-surface | Canvas (the UA <dialog> background) |
| --dlg-radius | 0.5rem |
| --dlg-font | system-ui, sans-serif |
| --dlg-backdrop | rgba(15, 23, 42, 0.4) |
| --dlg-header-bg | #1f2937 |
| --dlg-header-fg | white |
| --dlg-accent / --dlg-accent-hover / --dlg-accent-fg | #3b82f6 / #2563eb / white |
| --dlg-border | #d1d5db |
| --dlg-text | #374151 |
Shades derived from the dark header bar (its bottom border, the close-X, the
ghost button inside it) stay literal. They only make sense against a dark
header, so restyle them yourself if you set --dlg-header-bg to a light color.
API
| Export | What it is |
| --------------------- | ------------------------------------------------------------------- |
| HostDialogs | The element class. HostDialogs.instance is the mounted singleton. |
| defineHostDialogs | Guarded customElements.define, default tag host-dialogs. |
| dialogChromeStyles | The shared CSSResult for your own dialogs. |
| ctrlEnterSubmits | Keydown handler: Ctrl/Cmd+Enter submits the dialog's form. |
| makeDialogDraggable | Makes a <dialog> movable by a handle element. |
Develop
npm install
npm run typecheck
npm test
npm run buildPublishing runs from a version tag. Push v0.1.0 and the
publish workflow checks the tag against
package.json, runs the tests, and publishes to npm.
Origin
Extracted from easyDBAccess, where these dialogs run in a browser app and inside Electron.
License
MIT © Marc Cawood
