Rewrote all the docs - more noob friendly now.
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -1,66 +1,10 @@
|
||||
# DropdownMenu
|
||||
|
||||
A button that reveals a floating list of links or actions when clicked. Closes when the user clicks outside or presses Escape. Positioned below the trigger by default.
|
||||
A button that, when clicked, opens a small floating menu of links. Like a folder label on a filing cabinet — pull it and a list of options drops down. Closes automatically when you click elsewhere or press Escape.
|
||||
|
||||
---
|
||||
|
||||
## HTML structure
|
||||
|
||||
```
|
||||
div.relative.inline-block ← anchor for absolute positioning
|
||||
{trigger rendered inline} ← any IHtmxComponent (usually a Button)
|
||||
div.dropdown-menu.absolute... ← the floating panel; hidden by default
|
||||
div.w-48.rounded-md.border.bg-popover.shadow-md.p-1
|
||||
a.dropdown-item ← link item
|
||||
hr.dropdown-separator ← separator (when isSeparator=true)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSS mechanics
|
||||
|
||||
| Class | Effect |
|
||||
|---|---|
|
||||
| `hidden` (initial) on panel | Hides the dropdown until toggled by JS |
|
||||
| `absolute z-50` | Floats above surrounding content |
|
||||
| `top-full mt-1` | Placed below the trigger with a small gap |
|
||||
| `right-0` / `left-0` | Controlled by the `position` parameter |
|
||||
| `dropdown-item` | `flex items-center px-2 py-1.5 text-sm rounded hover:bg-accent cursor-pointer` |
|
||||
|
||||
---
|
||||
|
||||
## JavaScript (delegated click in `components.js`)
|
||||
|
||||
Set up once on `document` — works for HTMX-swapped dropdowns.
|
||||
|
||||
**Open / close toggle:**
|
||||
1. Click on the trigger element (`[data-dropdown-trigger]`) → toggle `.hidden` on the sibling `.dropdown-menu`
|
||||
2. Click outside the dropdown root → close all open dropdowns
|
||||
3. `Escape` keydown → close all open dropdowns
|
||||
4. Click on a `.dropdown-item` link → close the parent dropdown and follow the link
|
||||
|
||||
---
|
||||
|
||||
## Constructor signature
|
||||
|
||||
```csharp
|
||||
public DropdownMenu(
|
||||
IHtmxComponent trigger,
|
||||
IEnumerable<(string Label, string Href, bool IsSeparator)> items,
|
||||
string position = "right")
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
|---|---|
|
||||
| `trigger` | Any `IHtmxComponent` — shown as the visible toggle element |
|
||||
| `items` | Menu items; `IsSeparator=true` renders an `<hr>` (Label/Href ignored) |
|
||||
| `position` | `"right"` (default) aligns panel right edge; `"left"` aligns left edge |
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
|
||||
### User menu
|
||||
## Quick example
|
||||
|
||||
```csharp
|
||||
new DropdownMenu(
|
||||
@@ -69,24 +13,70 @@ new DropdownMenu(
|
||||
{
|
||||
("Profile", "/profile", false),
|
||||
("Settings", "/settings", false),
|
||||
("", "", true), // separator
|
||||
("", "", true), // separator line
|
||||
("Sign out", "/logout", false),
|
||||
})
|
||||
```
|
||||
|
||||
### Icon-button dropdown
|
||||
---
|
||||
|
||||
## All the options
|
||||
|
||||
```csharp
|
||||
public DropdownMenu(
|
||||
IHtmxComponent trigger,
|
||||
IEnumerable<(string Label, string Href, bool IsSeparator)> items,
|
||||
string position = "right")
|
||||
```
|
||||
|
||||
| Parameter | What it does |
|
||||
|---|---|
|
||||
| `trigger` | The visible button that opens the menu. Any `IHtmxComponent` — usually a `Button`. |
|
||||
| `items` | The list of menu items. Each is a `(Label, Href, IsSeparator)` tuple. |
|
||||
| `position` | `"right"` aligns the menu to the right edge of the trigger (default). `"left"` aligns it to the left. |
|
||||
|
||||
**Item tuple fields:**
|
||||
|
||||
| Field | What it does |
|
||||
|---|---|
|
||||
| `Label` | The text shown in the menu. |
|
||||
| `Href` | The URL to navigate to when clicked. |
|
||||
| `IsSeparator` | Set to `true` to render a divider line instead of a link. `Label` and `Href` are ignored. |
|
||||
|
||||
---
|
||||
|
||||
## Real-world examples
|
||||
|
||||
### User account menu in the header
|
||||
|
||||
```csharp
|
||||
new DropdownMenu(
|
||||
trigger: new Button("My account", variant: "ghost"),
|
||||
items: new[]
|
||||
{
|
||||
("Profile", "/profile", false),
|
||||
("Billing", "/billing", false),
|
||||
("", "", true),
|
||||
("Sign out", "/logout", false),
|
||||
})
|
||||
```
|
||||
|
||||
### Row action menu in a table (three-dot icon button)
|
||||
|
||||
```csharp
|
||||
new DropdownMenu(
|
||||
trigger: new Button("⋯", size: "icon", variant: "ghost"),
|
||||
items: new[]
|
||||
{
|
||||
("Edit", "/items/42/edit", false),
|
||||
("Delete", "/items/42/delete", false),
|
||||
})
|
||||
("Edit", $"/items/{item.Id}/edit", false),
|
||||
("Delete", $"/items/{item.Id}/delete", false),
|
||||
},
|
||||
position: "left") // avoid overflow near the right edge of the screen
|
||||
```
|
||||
|
||||
### Left-aligned dropdown (useful when near the right edge of the viewport)
|
||||
### Left-aligned menu (near right side of viewport)
|
||||
|
||||
Use `position: "left"` when the trigger is close to the right edge of the screen to prevent the menu from clipping off-screen:
|
||||
|
||||
```csharp
|
||||
new DropdownMenu(
|
||||
@@ -95,26 +85,15 @@ new DropdownMenu(
|
||||
position: "left")
|
||||
```
|
||||
|
||||
### HTMX action items
|
||||
|
||||
Items use `<a href>` — if you need HTMX requests, override by building the HTML manually:
|
||||
|
||||
```csharp
|
||||
// Pass a synthetic IHtmxComponent for trigger and use a raw slot override
|
||||
// for items that need hx-delete / hx-post, since items only support href links.
|
||||
// Alternatively, use a Dialog for confirmation dialogs linked from the dropdown.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tips and tricks
|
||||
## How it works
|
||||
|
||||
- The `trigger` is any `IHtmxComponent` — pass a `Button`, an `Avatar`, or any custom component.
|
||||
- All items are rendered as `<a href>` links. For actions that should POST/DELETE, either route them through normal GET links to a form redirect, or pair them with a confirmation Dialog.
|
||||
- For a context menu that appears at a table row, pass `new Button("⋯", size: "icon", variant: "ghost")` as the trigger.
|
||||
- Setting `position: "left"` prevents the dropdown from overflowing the right side of the viewport when the trigger is near the right edge.
|
||||
- Multiple dropdowns on the same page are handled independently — clicking one will close others.
|
||||
- Multiple dropdowns on the same page are handled independently — clicking one will close others.
|
||||
The menu panel is always present in the HTML but hidden with a `hidden` class. When the trigger is clicked, JavaScript toggles the `hidden` class to show it. Clicking anything outside — or pressing Escape — adds `hidden` back.
|
||||
|
||||
Because the click listener is attached to `document`, dropdown menus that are HTMX-swapped in work automatically.
|
||||
|
||||
All items are rendered as `<a href="...">` links. If you need an action that POSTs data (like a delete), the cleanest approach is to route it through a confirmation Dialog.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user