Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork1.6k
MudMenu: Replace IActivatable with MenuContext#12145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:dev
Are you sure you want to change the base?
Conversation
- Created MenuContext class to encapsulate menu activation logic- Updated MudMenu to use MenuContext instead of IActivatable- Added MenuContext as cascading parameter in MudBaseButton- Updated test to use MenuContext- All 3936 tests passing (61 menu tests, 0 failures)Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com>
Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com>
danielchalmers commentedNov 23, 2025
Donating this. I don't know enough about it to do any more work |
ScarletKuro commentedNov 25, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Never use Goal Replace the old non-async, tightly coupled, component-specific, cascading This new API must: Provide an elegant
Replace the activator pattern with a strongly typed context API: Replace: publicRenderFragment?ActivatorContent{get;set;} with: publicRenderFragment<MenuContext>?ActivatorContent{get;set;} Replace the old activator render code: <CascadingValueValue="@((IActivatable)this)"IsFixed="true"> <div@onclick="ToggleMenuAsync"disabled="@Disabled"class="@ActivatorClassname">@ActivatorContent </div></CascadingValue> with: <divdisabled="@Disabled"class="@ActivatorClassname">@ActivatorContent(new MenuContext(this))</div> Enable all components, HTML, custom components, anything — to trigger menu actions: Activators must now be written like this (just one of the example): <MudMenuFullWidth="true"PopoverClass="menu-popover-class"> <ActivatorContent> <MudButtonVariant="Variant.Filled"OnClick="@(() => context.OpenMenuAsync())"Color="Color.Primary"> Activator Content Standard </MudButton> </ActivatorContent> <ChildContent> <MudMenuItemClass="test-class">1</MudMenuItem> <MudMenuItemHref="https://www.test.com">2</MudMenuItem> <MudMenuItemHref="https://www.test.com"Target="_blank">3</MudMenuItem> </ChildContent></MudMenu> Where the consumer always interacts with the menu using the passed Remove the obsolete API entirely:
Updateall documentation & tests:
Required Copilot must NOT reuse old code. /// <summary>/// Exposes async operations for managing a <see cref="MudMenu"/> instance,/// including opening, closing, toggling, and closing parent menus./// </summary>publicsealedclassMenuContext{privatereadonlyMudMenu_menu;/// <summary>/// Creates a new <see cref="MenuContext"/> for the specified <see cref="MudMenu"/>./// </summary>publicMenuContext(MudMenumenu){_menu=menu;}/// <summary>/// Opens the menu. If <see cref="MudMenu.PositionAtCursor"/> is enabled,/// the menu is positioned using the supplied event arguments./// </summary>publicTaskOpenMenuAsync(EventArgs?args=null)=>_menu.OpenMenuAsync(args);/// <summary>Closes the menu.</summary>publicTaskCloseMenuAsync()=>_menu.CloseMenuAsync();/// <summary>Toggles the menu's visibility.</summary>publicTaskToggleMenuAsync(EventArgs?args=null)=>_menu.ToggleMenuAsync(args);/// <summary>Closes all parent menus in the hierarchy.</summary>publicTaskCloseAllMenusAsync()=>_menu.CloseAllMenusAsync();} Copilot may improve naming, add more XML doc details, or expand operations if needed. Copilot MUST update all relevant tests and docs. Things Copilot Must NOT Do:
|
- Create new MenuContext with async methods (OpenMenuAsync, CloseMenuAsync, ToggleMenuAsync, CloseAllMenusAsync)- Change ActivatorContent from RenderFragment? to RenderFragment<MenuContext>?- Remove CascadingValue pattern - context is passed directly to RenderFragment- Remove IActivatable dependency from MudMenu (kept for MudFileUpload)- Revert MudBaseButton changes - IActivatable remains for FileUpload- Update all doc examples and test components to use context.ToggleMenuAsync- Update tests to work with new context-based patternCo-authored-by: ScarletKuro <19953225+ScarletKuro@users.noreply.github.com>
- Restore@OnClick handler on activator div for backward compatibility- Add ArgumentNullException.ThrowIfNull validation in MenuContext constructorCo-authored-by: ScarletKuro <19953225+ScarletKuro@users.noreply.github.com>
Implemented the clean async MenuContext API as requested in commit Changes made:
No |
ScarletKuro commentedNov 25, 2025
Well that looks much better than the previous version... At least it looks more realistic and workable |
meenzen commentedNov 25, 2025
Doesn't look too bad, but api wise personally I prefer shorter names like |
ScarletKuro commentedNov 25, 2025
Fair |
ScarletKuro commentedDec 9, 2025
@copilot investigate and fix why this example with <MudMenu> <ActivatorContent> <MudButtonVariant="Variant.Filled"Color="Color.Primary"OnClick="@context.ToggleAsync">Button</MudButton> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu><MudMenu> <ActivatorContent> <MudChipT="string"Icon="@Icons.Material.Filled.Person"Color="Color.Primary"OnClick="@(() => context.ToggleAsync())">Chip</MudChip> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu><MudMenu> <ActivatorContent> <div@onclick="@context.ToggleAsync"style="cursor:pointer"> <MudAvatar> <MudImageSrc="images/toiletvisit.jpg" /> </MudAvatar> </div> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu> That example is located on I noticed that I thought it was because |
- Remove@OnClick from wrapper div to prevent event bubbling causing double-toggle- Cache MenuContext instance in MudMenu constructor instead of creating inline- Now only the explicit click handlers in ActivatorContent trigger menu toggleCo-authored-by: ScarletKuro <19953225+ScarletKuro@users.noreply.github.com>
CopilotAI commentedDec 9, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
... Fixed in commit Root cause: The wrapper div created by MudMenu had
Solution: Removed Also fixed: MenuContext is now cached in the constructor instead of being created inline on every render.
... Fixed in commit |
ScarletKuro commentedDec 9, 2025
@danielchalmers@versile2 the problem is fixed.
Not sure if that broke something else |
ScarletKuro commentedDec 9, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I see that in the |
ScarletKuro commentedDec 9, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@copilot I noticed after the recent change the right click example <MudMenuFullWidth="true"ActivationEvent="@MouseEvent.LeftClick"> <ActivatorContent> <MudChipT="string"Icon="@Icons.Material.Filled.Mouse"Color="Color.Primary"OnClick="@(() => context.ToggleAsync())">Left Click</MudChip> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> <MudMenuItemLabel="Sign Out" /> </ChildContent></MudMenu><MudMenuActivationEvent="@MouseEvent.RightClick"> <ActivatorContent> <div@oncontextmenu="@context.ToggleAsync"@oncontextmenu:preventDefault> <MudChipT="string"Icon="@Icons.Material.Filled.Mouse"Color="Color.Primary">Right Click</MudChip> </div> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> <MudMenuItemLabel="Sign Out" /> </ChildContent></MudMenu><MudMenuFullWidth="true"ActivationEvent="@MouseEvent.MouseOver"AnchorOrigin="Origin.BottomCenter"TransformOrigin="Origin.TopCenter"> <ActivatorContent> <div@onpointerenter="@context.OpenAsync"@onpointerleave="@context.CloseAsync"> <MudChipT="string"Icon="@Icons.Material.Filled.Mouse"Color="Color.Primary">Mouse Over</MudChip> </div> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> <MudMenuItemLabel="Sign Out" /> </ChildContent></MudMenu> This part is not working when clicking right click <MudMenuActivationEvent="@MouseEvent.RightClick"> <ActivatorContent> <div@oncontextmenu="@context.ToggleAsync"@oncontextmenu:preventDefault> <MudChipT="string"Icon="@Icons.Material.Filled.Mouse"Color="Color.Primary">Right Click</MudChip> </div> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> <MudMenuItemLabel="Sign Out" /> </ChildContent></MudMenu> It used to work. <MudCard> <MudCardHeader> <CardHeaderContent> <MudTextTypo="Typo.body1">Istra Croatia</MudText> <MudTextTypo="Typo.body2">Peninsula in Europe</MudText> </CardHeaderContent> </MudCardHeader> <div@onclick="@(OpenContextMenu)"@oncontextmenu="@(OpenContextMenu)"@oncontextmenu:preventDefault@oncontextmenu:stopPropagation> <MudCardMediaImage="images/pilars.jpg"Height="250"/> </div> <MudCardContent> <MudTextTypo="Typo.body2">Try left clicking or right clicking the image to open the menu.</MudText> </MudCardContent></MudCard><MudMenuPositionAtCursor@ref="_contextMenu"> <MudMenuItemLabel="Undo" /> <MudMenuItemLabel="Redo"Disabled /> <MudDivider /> <MudMenuItemLabel="Cut" /> <MudMenuItemLabel="Copy" /> <MudMenuItemLabel="Paste" /></MudMenu>@code{#nullable enableprivateMudMenu_contextMenu=null!;privateasyncTaskOpenContextMenu(MouseEventArgsargs){await_contextMenu.OpenMenuAsync(args);}} The left and right click works just as fine. |
- Disable root div's contextmenu handler when ActivatorContent is present- Prevents double-toggle when user's ActivatorContent has explicit right-click handler- Update test to trigger contextmenu on user's div instead of MudMenu's wrapper divCo-authored-by: ScarletKuro <19953225+ScarletKuro@users.noreply.github.com>
... Fixed in commit Root cause: The root div's Solution: Added |
ScarletKuro commentedDec 9, 2025
@danielchalmers@versile2 This is now fixed as well. You two have worked with Menu more than I have, so I can’t really judge whether the fix is good The problem was that both. <div@oncontextmenu="@context.ToggleAsync"@oncontextmenu:preventDefault><MudChipT="string"Icon="@Icons.Material.Filled.Mouse"Color="Color.Primary">Right Click</MudChip></div> And the MudMenu: |
meenzen left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I feel like we should maybe have a component like<MudTarget> instead of a bunch of low-level divs
So instead of
<MudMenu> <ActivatorContent> <div@onclick="@context.ToggleAsync"style="cursor:pointer"> <MudAvatar> <MudImageSrc="images/toiletvisit.jpg" /> </MudAvatar> </div> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu>
We could have something like this:
<MudMenu> <ActivatorContent> <MudTargetEvent="@MouseEvent.LeftClick"OnEvent="@context.ToggleAsync"> <MudAvatar> <MudImageSrc="images/toiletvisit.jpg" /> </MudAvatar> </MudTarget> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu>
versile2 commentedDec 10, 2025
I'm not opposed necessarily but the point of this is showing them how to access the context. What would MudTarget get us? Some components we expose more than a single event in the context. Pros/Cons? Our goal is just giving them a universal way to trigger the context and/or access properties on the context. Also on this example I'd probably make this a MudButton instead of a div so you got benefit of ripple etc. |
ScarletKuro commentedDec 10, 2025
Yeah, I'm also not entirely sure about |
meenzen commentedDec 10, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I just find the way this works using divs very unintuitive. And this PR now adds a bunch of examples where that's needed when before there was only one example that worked like this. It's really easy to forget things like adding There's many cases where I've needed this before, like wrapping a |
danielchalmers commentedDec 11, 2025
adding a shared class name to the divs might be more intuitive and somewhat self-documenting (ex: |
versile2 commentedDec 11, 2025
I would say just wrapping that in a MudButton would work great. The goal is simplicity. We just give them the context action they do with it what they want. Just like MudStepper and a few other controls. <MudMenu> <ActivatorContent> <MudButtonOnClick="@context.ToggleAsync"> <MudAvatar> <MudImageSrc="images/toiletvisit.jpg" /> </MudAvatar> </MudButton> </ActivatorContent> <ChildContent> <MudMenuItemLabel="Profile" /> <MudMenuItemLabel="Theme" /> <MudMenuItemLabel="Usage" /> </ChildContent></MudMenu> |
versile2 commentedDec 11, 2025
Also tag me when it's ready for review if you want@ScarletKuro |
ScarletKuro commentedDec 11, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
It's ready. If someone wishing to do a
Don't think I understood. |
danielchalmers commentedDec 15, 2025
-<div @onclick="@context.ToggleAsync">+<div @onclick="@context.ToggleAsync"> just for the sake of identifying it |
Uh oh!
There was an error while loading.Please reload this page.
Replaces the generic
IActivatableinterface with a dedicated asyncMenuContextclass for MudMenu activation, providing a clean, modern, component-agnostic API.Changes:
MenuContextclass - Clean async API withOpenAsync,CloseAsync,ToggleAsync, andCloseAllAsyncmethods. Fully XML-documented with proper null validation.ActivatorContentsignature changed - FromRenderFragment?toRenderFragment<MenuContext>?allowing context to be passed directly to custom activatorsMudMenurefactored - RemovedIActivatableinterface and CascadingValue pattern. Context passed directly to RenderFragment and cached in constructor for performance.IActivatablepreserved forMudFileUpload- Kept inMudBaseButtonfor backward compatibility with file upload componentcontext.ToggleAsync()callsNew MenuContext API:
New usage pattern:
Key improvements:
async voidor.CatchAndLog()Taskwith proper async patternsToggleAsyncinstead ofToggleMenuAsync)Breaking change:
ActivatorContentnow requires explicit context usage (e.g.,context.ToggleAsync()). Users must explicitly wire up click/contextmenu handlers in their ActivatorContent.Checklist:
Original prompt
✨ Let Copilot coding agentset things up for you — coding agent works faster and does higher quality work when set up for your repo.