diff --git a/Marechai/Pages/_Host.cshtml b/Marechai/Pages/_Host.cshtml index bc983a30..2095aba7 100644 --- a/Marechai/Pages/_Host.cshtml +++ b/Marechai/Pages/_Host.cshtml @@ -155,5 +155,6 @@ + diff --git a/Marechai/Shared/SearchBar.razor b/Marechai/Shared/SearchBar.razor index d2424fbd..17ed7f4d 100644 --- a/Marechai/Shared/SearchBar.razor +++ b/Marechai/Shared/SearchBar.razor @@ -10,7 +10,9 @@ @inject SearchService SearchService @inject NavigationManager Nav @inject StringLocalizer L +@inject IJSRuntime JS +
@@ -81,12 +84,18 @@ +
@code { SearchResultDto _selected; string _lastQuery = string.Empty; MudAutocomplete _autocomplete; + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if(firstRender) await JS.InvokeVoidAsync("SearchDropdownPositioning.attach", "search-bar-anchor"); + } + Task> SearchAsync(string value, CancellationToken ct) => SearchAsyncCore(value, ct); diff --git a/Marechai/wwwroot/css/site.css b/Marechai/wwwroot/css/site.css index 9154edb3..c1fdff27 100644 --- a/Marechai/wwwroot/css/site.css +++ b/Marechai/wwwroot/css/site.css @@ -300,10 +300,13 @@ } /* Full-width search results dropdown in top bar. - MudAutocomplete normally anchors the popover to match the input width. - This class overrides that to span from the search box to the right edge of the screen. */ + These are just the initial values for first paint — MudBlazor's own JS + actively rewrites this popover's inline `left`/`width` on open, resize, and + via its internal flip/collision logic, fighting any CSS-only override. + js/search-dropdown-positioning.js watches this element and re-asserts + `left` (anchored to the search box) and `right: 0; width: auto` (stretched + to the viewport's right edge) every time MudBlazor changes it. */ .search-popover-fullwidth { - left: auto !important; right: 0 !important; width: auto !important; max-width: none !important; diff --git a/Marechai/wwwroot/js/search-dropdown-positioning.js b/Marechai/wwwroot/js/search-dropdown-positioning.js new file mode 100644 index 00000000..29d9e54e --- /dev/null +++ b/Marechai/wwwroot/js/search-dropdown-positioning.js @@ -0,0 +1,62 @@ +// Keeps the top-bar search results popover anchored to the search box's own +// left edge while letting it stretch to the right edge of the viewport. +// +// MudAutocomplete's popover JS actively re-measures and rewrites the popover's +// inline `left`/`width` on open, on resize, and via its own collision-avoidance +// ("flip") logic — it does this regardless of any CSS we apply, so a CSS-only +// `left` override gets clobbered moments after render. We instead watch the +// popover element itself and correct its inline style back to "anchored to the +// search box, stretched to the right edge" every time MudBlazor changes it. +window.SearchDropdownPositioning = (function () { + let attached = false; + + function reposition(anchor, popover) { + const desiredLeft = anchor.getBoundingClientRect().left + "px"; + + if (popover.style.left !== desiredLeft) popover.style.setProperty("left", desiredLeft, "important"); + if (popover.style.right !== "0px") popover.style.setProperty("right", "0px", "important"); + if (popover.style.width !== "auto") popover.style.setProperty("width", "auto", "important"); + if (popover.style.maxWidth !== "none") popover.style.setProperty("max-width", "none", "important"); + } + + function watch(anchor, popover) { + reposition(anchor, popover); + + // Re-assert our positioning whenever MudBlazor's own JS touches the + // popover's style/class (open, close, resize, flip recalculation). + // Each call is a no-op once values already match, so this can't loop. + new MutationObserver(() => reposition(anchor, popover)) + .observe(popover, { attributes: true, attributeFilter: ["style", "class"] }); + + window.addEventListener("resize", () => reposition(anchor, popover)); + } + + return { + attach: function (anchorId) { + if (attached) return; + + const anchor = document.getElementById(anchorId); + if (!anchor) return; + + const existing = document.querySelector(".search-popover-fullwidth"); + if (existing) { + watch(anchor, existing); + attached = true; + return; + } + + // The popover element doesn't exist until MudBlazor first renders + // it (typically on first focus/keystroke), so wait for it to show + // up in the DOM before attaching the real observer. + const bodyObserver = new MutationObserver(() => { + const popover = document.querySelector(".search-popover-fullwidth"); + if (!popover) return; + + bodyObserver.disconnect(); + watch(anchor, popover); + attached = true; + }); + bodyObserver.observe(document.body, { childList: true, subtree: true }); + } + }; +})();