diff --git a/src/Aspire.Dashboard/Components/Controls/ExceptionDetails.razor b/src/Aspire.Dashboard/Components/Controls/ExceptionDetails.razor index 90b4e0ee013..9f734b761f1 100644 --- a/src/Aspire.Dashboard/Components/Controls/ExceptionDetails.razor +++ b/src/Aspire.Dashboard/Components/Controls/ExceptionDetails.razor @@ -1,52 +1,35 @@ @namespace Aspire.Dashboard.Components @using Aspire.Dashboard.Extensions +@using Aspire.Dashboard.Components.Dialogs @using Aspire.Dashboard.Otlp.Model @using Aspire.Dashboard.Resources @inject IStringLocalizer Loc -@{ - var iconId = Guid.NewGuid().ToString(); - @* - * We don't want a browser tooltip to display when hovering over the error icon. - * Use alt instead of title for screen readers. Also, the column has a title set - * so add a whitespace title to prevent a browser tooltip from displaying. - *@ - var title = " "; -} - @if (!string.IsNullOrWhiteSpace(ExceptionText)) { - - - -
-

@Loc[nameof(ControlsStrings.ExceptionDetailsTitle)]

- -
@ExceptionText
- -
- - @Loc[nameof(ControlsStrings.GridValueCopyToClipboard)] - - - -
-
-
+ + + } @code { [Parameter, EditorRequired] public required string ExceptionText { get; set; } + + [CascadingParameter] + public required ViewportInformation ViewportInformation { get; init; } + + [Inject] + public required IDialogService DialogService { get; init; } + + private void OnExceptionDetailsClicked(MouseEventArgs e) + { + _ = TextVisualizerDialog.OpenDialogAsync(ViewportInformation, DialogService, Loc[nameof(ControlsStrings.ExceptionDetailsTitle)], ExceptionText); + } } diff --git a/src/Aspire.Dashboard/Components/Controls/GridValue.razor b/src/Aspire.Dashboard/Components/Controls/GridValue.razor index 2e96b8db387..60a243d3fca 100644 --- a/src/Aspire.Dashboard/Components/Controls/GridValue.razor +++ b/src/Aspire.Dashboard/Components/Controls/GridValue.razor @@ -3,25 +3,27 @@ @inject IStringLocalizer DialogsLoc
+ + @* Value area *@ + @if (EnableMasking && IsMasked) { - + ●●●●●●●● } - else if (EnableHighlighting) - { - - @ContentBeforeValue - - @ContentAfterValue - - } else { - + @ContentBeforeValue - @(MaxDisplayLength.HasValue ? TrimLength(Value) : Value) + @if (EnableHighlighting && !string.IsNullOrEmpty(HighlightText)) + { + + } + else + { + @Value + } @ContentAfterValue } @@ -35,46 +37,52 @@ ]; } -
- - - + @* Button area *@ - - - - - - @PreCopyToolTip - +
- - - - + + + + - @DialogsLoc[nameof(Dialogs.OpenInTextVisualizer)] - - -
+ + + + + + @PreCopyToolTip + - @if (EnableMasking) - { -
+ + + + + + @DialogsLoc[nameof(Dialogs.OpenInTextVisualizer)] + + + + + @if (ContentInButtonArea is not null) + { + @ContentInButtonArea + } + + @if (EnableMasking) + { -
- } + } + +
diff --git a/src/Aspire.Dashboard/Components/Controls/GridValue.razor.cs b/src/Aspire.Dashboard/Components/Controls/GridValue.razor.cs index 16dcf85e057..26e4b93d66e 100644 --- a/src/Aspire.Dashboard/Components/Controls/GridValue.razor.cs +++ b/src/Aspire.Dashboard/Components/Controls/GridValue.razor.cs @@ -30,6 +30,12 @@ public partial class GridValue [Parameter] public RenderFragment? ContentAfterValue { get; set; } + /// + /// Content to include, if any, in button area to right. Intended for adding extra buttons. + /// + [Parameter] + public RenderFragment? ContentInButtonArea { get; set; } + /// /// If set, copies this value instead of . /// @@ -66,9 +72,6 @@ public partial class GridValue [Parameter] public EventCallback IsMaskedChanged { get; set; } - [Parameter] - public int? MaxDisplayLength { get; set; } - [Parameter] public string? ToolTip { get; set; } @@ -99,16 +102,6 @@ private async Task ToggleMaskStateAsync() await IsMaskedChanged.InvokeAsync(IsMasked); } - private string TrimLength(string? text) - { - if (text is not null && MaxDisplayLength is int maxLength && text.Length > maxLength) - { - return text[..maxLength]; - } - - return text ?? ""; - } - private void ToggleMenuOpen() { _isMenuOpen = !_isMenuOpen; diff --git a/src/Aspire.Dashboard/Components/Controls/GridValue.razor.css b/src/Aspire.Dashboard/Components/Controls/GridValue.razor.css index 3fac69065a2..d23af45195b 100644 --- a/src/Aspire.Dashboard/Components/Controls/GridValue.razor.css +++ b/src/Aspire.Dashboard/Components/Controls/GridValue.razor.css @@ -9,7 +9,7 @@ grid-template-columns: 1fr auto auto; } -::deep .cellText { +::deep .grid-value { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; @@ -19,9 +19,10 @@ opacity: 0; cursor: pointer; width: 0; + display: inline-block; } -::deep:hover .defaultHidden, ::deep:focus-within .defaultHidden { +::deep:hover .defaultHidden, ::deep:focus-within .defaultHidden { opacity: 1; /* safari has a bug where hover is not always called on an invisible element, so we use opacity instead */ width: auto; } diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor index 8e76809c0de..b19d704ad48 100644 --- a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor +++ b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor @@ -166,9 +166,9 @@ EnableHighlighting="@(!string.IsNullOrEmpty(_filter))" HighlightText="@_filter" TextVisualizerTitle="@context.Name"> - + - + diff --git a/src/Aspire.Dashboard/Components/ResourcesGridColumns/LogMessageColumnDisplay.razor b/src/Aspire.Dashboard/Components/ResourcesGridColumns/LogMessageColumnDisplay.razor index 0149640685a..a7b3ae239a7 100644 --- a/src/Aspire.Dashboard/Components/ResourcesGridColumns/LogMessageColumnDisplay.razor +++ b/src/Aspire.Dashboard/Components/ResourcesGridColumns/LogMessageColumnDisplay.razor @@ -10,9 +10,9 @@ ValueDescription="@Loc[nameof(StructuredLogs.StructuredLogsMessageColumnHeader)]" EnableHighlighting="true" HighlightText="@FilterText"> - + - + @code { diff --git a/src/Aspire.Dashboard/wwwroot/js/app.js b/src/Aspire.Dashboard/wwwroot/js/app.js index 8945be7ceed..5e0939e04f8 100644 --- a/src/Aspire.Dashboard/wwwroot/js/app.js +++ b/src/Aspire.Dashboard/wwwroot/js/app.js @@ -133,6 +133,7 @@ window.copyTextToClipboard = function (id, text, precopy, postcopy) { const copyIcon = button.querySelector('.copy-icon'); const checkmarkIcon = button.querySelector('.checkmark-icon'); + const anchoredTooltip = document.querySelector(`fluent-tooltip[anchor="${id}"]`); const tooltipDiv = anchoredTooltip ? anchoredTooltip.children[0] : null; navigator.clipboard.writeText(text) @@ -140,8 +141,10 @@ window.copyTextToClipboard = function (id, text, precopy, postcopy) { if (tooltipDiv) { tooltipDiv.innerText = postcopy; } - copyIcon.style.display = 'none'; - checkmarkIcon.style.display = 'inline'; + if (copyIcon && checkmarkIcon) { + copyIcon.style.display = 'none'; + checkmarkIcon.style.display = 'inline'; + } }) .catch(() => { if (tooltipDiv) { @@ -154,8 +157,10 @@ window.copyTextToClipboard = function (id, text, precopy, postcopy) { tooltipDiv.innerText = precopy; } - copyIcon.style.display = 'inline'; - checkmarkIcon.style.display = 'none'; + if (copyIcon && checkmarkIcon) { + copyIcon.style.display = 'inline'; + checkmarkIcon.style.display = 'none'; + } delete button.dataset.copyTimeout; }, 1500); };