Rewrote all the docs - more noob friendly now.
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
+51
-92
@@ -1,53 +1,32 @@
|
||||
# Dialog
|
||||
|
||||
A modal dialog using the native HTML `<dialog>` element. Content is organized into optional title, description, body, and footer sections. Open/close is handled by client-side JS via delegated click events on `data-dialog-open` and `data-dialog-close` attributes.
|
||||
A modal pop-up window that appears on top of the page. Think of it like a small piece of paper sliding onto the desk — the rest of the page dims and you have to deal with the dialog before you can go back to work.
|
||||
|
||||
Opening and closing is handled entirely with `data-dialog-open` and `data-dialog-close` HTML attributes — no custom JavaScript needed.
|
||||
|
||||
---
|
||||
|
||||
## HTML structure
|
||||
## Quick example
|
||||
|
||||
```
|
||||
dialog[id, class=...]
|
||||
div.dialog-panel.relative.bg-background.p-6.rounded-lg.shadow-xl.w-full.max-w-md...
|
||||
button.absolute.top-4.right-4[data-dialog-close={id}] ← × close button
|
||||
h2.text-lg.font-semibold ← title (omitted when empty)
|
||||
p.text-sm.text-muted-foreground.mt-1 ← description (omitted when empty)
|
||||
div.mt-4 ← body content
|
||||
{content}
|
||||
div.mt-6.flex.justify-end.gap-2 ← footer (omitted when empty)
|
||||
{footer}
|
||||
```csharp
|
||||
new Dialog(
|
||||
id: "about-dialog",
|
||||
title: "About this app",
|
||||
content: "<p>Version 1.0 — built with .NET 10.</p>",
|
||||
footer: """<button data-dialog-close="about-dialog">Close</button>""")
|
||||
```
|
||||
|
||||
---
|
||||
Then somewhere on the page, add a trigger:
|
||||
|
||||
## CSS mechanics
|
||||
```html
|
||||
<button data-dialog-open="about-dialog">About</button>
|
||||
```
|
||||
|
||||
| Class | Effect |
|
||||
|---|---|
|
||||
| `dialog::backdrop` (in `input.css`) | Semi-transparent black overlay behind the dialog |
|
||||
| `animate-in fade-in-0 zoom-in-95` | CSS entry animation when dialog opens |
|
||||
| `max-w-md w-full` | Responsive: full width on small screens, capped at `md` |
|
||||
| `overflow-y-auto max-h-[90vh]` | Scrollable body for tall content |
|
||||
That's it. No JavaScript needed in your templates.
|
||||
|
||||
---
|
||||
|
||||
## JavaScript (delegated clicks in `components.js`)
|
||||
|
||||
Set up once on `document` and works for all dialogs on the page, including those HTMX-swapped in.
|
||||
|
||||
### Open
|
||||
Any element with `data-dialog-open="myDialogId"` calls `document.getElementById('myDialogId').showModal()`.
|
||||
|
||||
### Close
|
||||
Any element with `data-dialog-close="myDialogId"` calls `document.getElementById('myDialogId').close()`.
|
||||
|
||||
The `×` close button inside the dialog panel already has `data-dialog-close` set to the dialog's id.
|
||||
|
||||
Clicking the `::backdrop` (outside the panel) also closes the dialog — the click handler checks whether the click target is the `<dialog>` element itself.
|
||||
|
||||
---
|
||||
|
||||
## Constructor signature
|
||||
## All the options
|
||||
|
||||
```csharp
|
||||
public Dialog(
|
||||
@@ -58,96 +37,76 @@ public Dialog(
|
||||
string footer = "")
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
| Parameter | What it does |
|
||||
|---|---|
|
||||
| `id` | Element id — must be unique per page; also used by `data-dialog-open` |
|
||||
| `content` | Raw HTML for the dialog body |
|
||||
| `title` | Optional heading at the top of the panel |
|
||||
| `description` | Optional subheading below the title |
|
||||
| `footer` | Optional raw HTML for the bottom button row |
|
||||
| `id` | A unique identifier. Used both on the `<dialog>` element and in `data-dialog-open`. |
|
||||
| `content` | The body of the dialog. Raw HTML. |
|
||||
| `title` | Optional bold heading at the top of the dialog panel. |
|
||||
| `description` | Optional smaller text below the title. |
|
||||
| `footer` | Optional button row at the bottom. Raw HTML. |
|
||||
|
||||
The title, description, and footer sections are omitted entirely from the HTML when not provided.
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
## Real-world examples
|
||||
|
||||
### Simple information dialog
|
||||
### Confirmation before a destructive action
|
||||
|
||||
```csharp
|
||||
// In the page component:
|
||||
Dialog = new Dialog(
|
||||
id: "about-dialog",
|
||||
title: "About BeepBoop",
|
||||
description: "A fast AOT-safe HTMX framework.",
|
||||
content: "<p>Version 1.0 — built with ❤️ and .NET 10.</p>",
|
||||
footer: """<button data-dialog-close="about-dialog" class="...">Close</button>""");
|
||||
```
|
||||
|
||||
Trigger button anywhere on the page:
|
||||
Place the dialog in the page template, then trigger it from a button:
|
||||
|
||||
```html
|
||||
<button data-dialog-open="about-dialog" class="...">About</button>
|
||||
<!-- Templates/ItemsPage.htmx -->
|
||||
$$DeleteDialog$$
|
||||
<!-- ... rest of page ... -->
|
||||
<button data-dialog-open="confirm-delete">Delete item</button>
|
||||
```
|
||||
|
||||
### Confirmation dialog
|
||||
|
||||
```csharp
|
||||
new Dialog(
|
||||
// Templates/ItemsPage.htmx.cs
|
||||
_deleteDialog = new Dialog(
|
||||
id: "confirm-delete",
|
||||
title: "Delete item",
|
||||
content: "<p>This action cannot be undone.</p>",
|
||||
footer: """
|
||||
<button data-dialog-close="confirm-delete" class="...">Cancel</button>
|
||||
<button hx-delete="/items/42" hx-confirm="" data-dialog-close="confirm-delete"
|
||||
<button data-dialog-close="confirm-delete">Cancel</button>
|
||||
<button hx-delete="/items/42" data-dialog-close="confirm-delete"
|
||||
class="bg-destructive text-destructive-foreground ...">
|
||||
Delete
|
||||
</button>
|
||||
""")
|
||||
""");
|
||||
```
|
||||
|
||||
### HTMX-powered content reload
|
||||
### Dialog that loads content on demand
|
||||
|
||||
Use HTMX's `revealed` trigger to load the dialog body only when it opens:
|
||||
|
||||
```csharp
|
||||
new Dialog(
|
||||
id: "user-detail",
|
||||
title: "User details",
|
||||
content: """<div hx-get="/users/42/detail" hx-trigger="revealed"></div>""")
|
||||
```
|
||||
|
||||
The `revealed` trigger fires when the dialog becomes visible, loading content on demand.
|
||||
### Closing from outside the dialog
|
||||
|
||||
### Embedding inside a page slot
|
||||
Any element anywhere on the page can close a dialog by setting `data-dialog-close`:
|
||||
|
||||
```html
|
||||
<!-- MyPage.htmx -->
|
||||
$$DeleteDialog$$
|
||||
<button data-dialog-open="confirm-delete" class="...">Delete</button>
|
||||
```
|
||||
|
||||
```csharp
|
||||
public IHtmxComponent DeleteDialog { get; }
|
||||
|
||||
public MyPage()
|
||||
{
|
||||
DeleteDialog = new Dialog(
|
||||
id: "confirm-delete",
|
||||
title: "Confirm deletion",
|
||||
content: "<p>Are you sure?</p>",
|
||||
footer: """<button data-dialog-close="confirm-delete">Cancel</button>""");
|
||||
}
|
||||
|
||||
protected override void RenderDeleteDialog(HtmxRenderContext ctx)
|
||||
=> DeleteDialog.Render(ctx.Next());
|
||||
<button data-dialog-close="confirm-delete">Never mind</button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tips and tricks
|
||||
## How it works
|
||||
|
||||
- The `id` is used both on the `<dialog>` element and in `data-dialog-open`/`data-dialog-close` — keep it unique per page.
|
||||
- The `×` close button is always rendered; `data-dialog-close` on footer buttons is optional but improves UX.
|
||||
- Use the native `<dialog>` `close` event for any cleanup needed after dismissal: `document.getElementById('id').addEventListener('close', fn)`.
|
||||
- Dialog content, title, description, and footer are raw HTML — HTML-encode user-supplied values.
|
||||
- For dialogs that load content via HTMX, place the HTMX attributes on a child div inside `content` rather than on the `<dialog>` element itself to avoid interfering with the dialog open/close lifecycle.
|
||||
- For dialogs that load content via HTMX, place the HTMX attributes on a child div inside `content` rather than on the `<dialog>` element itself to avoid interfering with the dialog open/close lifecycle.
|
||||
Dialog uses the native HTML `<dialog>` element with `showModal()`. A backdrop (the dark overlay) comes from the browser's built-in `::backdrop` pseudo-element, styled in `input.css`.
|
||||
|
||||
JavaScript in `components.js` listens for clicks anywhere on the page. If the clicked element has `data-dialog-open`, it calls `showModal()` on the matching dialog. If it has `data-dialog-close`, it calls `close()`. Clicking outside the dialog panel (on the backdrop) also closes it.
|
||||
|
||||
Because the listener is on `document`, dialogs that are HTMX-swapped in work automatically without any re-initialisation.
|
||||
|
||||
All `content` and `footer` strings are raw HTML — HTML-encode any user-supplied values before passing them in.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user