Components
Context menu
The menu a dropdown opens, opened by a right-click instead: the first child of Ui.Context is the area, the second the Ui.Menu, with the same rows and the same keyboard.
Drawn and behaving as Flux UI's context menu does, measured against its live docs.
Context menu
Right-click the area. The runtime opens the menu at the pointer at once, on either host, and the ContextMenu key or Shift+F10 opens it at the focused element. iOS never fires the event, so nothing in it should be the only way to do something.
namespace Rask.Site.Features.UiDocs.Examples;
public sealed partial class ContextExample : Component
{
private string _sort = "name";
private string _last = "nothing yet";
protected override Component? Render() =>
Div.Class("space-y-4")[
Ui.Context[
// Focusable, so the ContextMenu key and Shift+F10 open it too.
Div.TabIndex(0)
.Class("inline-block rounded-lg border-2 border-dashed border-zinc-300 px-16 py-6 text-sm "
+ "text-zinc-500 dark:border-zinc-600 dark:text-zinc-400")["Right click"],
Ui.Menu[
Ui.MenuItem.Icon(Ui.IconName.Plus).OnClick(() => { _last = "new post"; })["New post"],
Ui.MenuSeparator.Key("s1"),
Ui.MenuSubmenu.Heading("Sort by")[
Ui.MenuRadioGroup.Value(_sort).OnChange(sort => { _sort = sort; _last = "sorted by " + sort; })[
Ui.MenuRadio.Value("name")["Name"],
Ui.MenuRadio.Value("date")["Date"],
Ui.MenuRadio.Value("popularity")["Popularity"]
]
],
Ui.MenuSeparator.Key("s2"),
Ui.MenuItem.Danger.Icon(Ui.IconName.Trash).OnClick(() => { _last = "deleted"; })["Delete"]
]
],
Ui.Text[$"Last action: {_last}."]
];
}Last action: nothing yet.
Behaviour
Opening
The first child of Ui.Context is the area that is right-clicked, and the second is the Ui.Menu, the same one a Ui.Dropdown opens, so the rows and the keyboard are identical: only the opening differs.
The runtime opens it: an element carrying data-rask-contextmenu="<popover id>" shows that popover at the pointer in place of the browser's menu, straight away and on either host. A round trip first would be a lag felt on every right-click.
Nothing in a context menu should be the only way to do something: iOS Safari never fires the event, so put the same actions somewhere visible too.
Where it lands
Where Flux's does: below the pointer, with the menu's end edge on it. Near an edge of the viewport it opens over the pointer, or with its start edge on it, and is slid back to stay 5px inside.
Position (BottomEnd, BottomCenter, BottomStart, TopEnd, TopCenter, TopStart), Gap and Offset((x, y)) move it. Target("id") opens a Ui.Menu.Id("id") written somewhere else; Detail lands on the menu as data-detail; Disabled leaves the browser's own menu alone.
While it is open the page behind neither scrolls nor takes the pointer (data-rask-lock), as for a dropdown's menu.
Keyboard
The ContextMenu key and Shift+F10 open it at the focused element, which is why the area is focusable. Unlike Flux's, the menu takes focus as it opens, so the arrow keys work at once; from there every key is the dropdown's.
Reference
Ui.Context
Opens its second child, a Ui.Menu, at the pointer when its first child is right-clicked.
Step | Takes | What it does |
|---|---|---|
.Position(…) | Ui.ContextPositionBottomEnd BottomCenter BottomStart TopEnd TopCenter TopStart Each is a step of its own too, unless an HTML tag has its name. | Where the menu opens against the pointer. Below it, its end edge on it, when unset. |
.Gap(…) | int | The distance between the pointer and the menu, in pixels. |
.Offset(…) | (int X, int Y) | Shifts the menu from where it would open, in pixels. |
.Target(…) | string | The id of a Ui.Menu written somewhere else, for one that cannot sit inside. |
.Detail(…) | string | A value for the menu's data-detail, to tell which area opened it. |
.Disabled() | bool | Leaves a right-click to the browser's own menu. |
.Open() | bool | Hands the open state to the page. Unset, the reader opens and closes it. |
.OnToggle(…) | a handler given the state it is now in: open => … | Runs when the reader opens or closes it. |