Rewrote all the docs - more noob friendly now.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-05 23:55:26 +05:00
parent e483bf73e7
commit f6ae86617c
35 changed files with 2159 additions and 2341 deletions
+51 -92
View File
@@ -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.
---