mirror of
https://github.com/marcominerva/SqlDatabaseVectorSearch.git
synced 2026-08-04 09:48:57 +00:00
Improve copy-to-clipboard UX in Ask.razor
Refactored the copy button to use per-message state, displaying a checkmark icon and "Copied!" tooltip when a message is copied. Simplified state management, removed unused references, and updated the Reset method to clear copied state. Enhanced Ask.razor.css to minimize button chrome and improve interactive states for a cleaner UI.
This commit is contained in:
@@ -77,10 +77,10 @@
|
||||
<Icon Class="d-flex" Name="IconName.CashCoin"></Icon>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="text-end bg-transparent">
|
||||
<Tooltip Title="@toolTipText" Color="TooltipColor.Dark" Placement="TooltipPlacement.Bottom">
|
||||
<Button Type="ButtonType.Button" Outline="false" @onclick="@(async () => await CopyToClipboardAsync(message.Text))">
|
||||
@if (showCopyConfirmation)
|
||||
<div class="text-end bg-transparent copy-button">
|
||||
<Tooltip Title="@(copiedMessage == message ? "Copied!" : "Copy to clipboard")" Color="TooltipColor.Dark" Placement="TooltipPlacement.Bottom">
|
||||
<Button Type="ButtonType.Button" Outline="false" @onclick="@(async () => await CopyToClipboardAsync(message))">
|
||||
@if (copiedMessage == message)
|
||||
{
|
||||
<Icon Name="IconName.Check" Class="text-success" />
|
||||
}
|
||||
@@ -113,11 +113,11 @@
|
||||
</span>
|
||||
<input @ref="askInput" type="text" @bind="@question" @bind:event="oninput" placeholder="Ask a question about your documents..." class="form-control border-0" maxlength="2000" @onkeydown="HandleKeyDown" />
|
||||
<div class="input-group-text bg-transparent border-0">
|
||||
<Button Type="ButtonType.Submit" @ref="askButton" Color="ButtonColor.Primary" Disabled="@(isAsking || string.IsNullOrWhiteSpace(question))" @onclick="AskQuestion">
|
||||
<Button Type="ButtonType.Submit" Color="ButtonColor.Primary" Disabled="@(isAsking || string.IsNullOrWhiteSpace(question))" @onclick="AskQuestion">
|
||||
<Icon Name="IconName.Send" />
|
||||
</Button>
|
||||
<Tooltip Title="Start a new conversation" Color="TooltipColor.Secondary" Placement="TooltipPlacement.Bottom">
|
||||
<Button Type="ButtonType.Reset" @ref="resetButton" Class="ms-2" Color="ButtonColor.Secondary" Disabled="@isAsking" @onclick="Reset">
|
||||
<Button Type="ButtonType.Reset" Class="ms-2" Color="ButtonColor.Secondary" Disabled="@isAsking" @onclick="Reset">
|
||||
<Icon CustomIconName="bi bi-x-lg" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
@@ -128,8 +128,6 @@
|
||||
|
||||
@code
|
||||
{
|
||||
private Button askButton = default!;
|
||||
private Button resetButton = default!;
|
||||
private ElementReference askInput = default!;
|
||||
private ElementReference chat = default!;
|
||||
|
||||
@@ -137,10 +135,9 @@
|
||||
private string? question;
|
||||
|
||||
private Guid conversationId = Guid.NewGuid();
|
||||
private bool isAsking = false;
|
||||
private bool isAsking;
|
||||
|
||||
private bool showCopyConfirmation = false;
|
||||
private string toolTipText = "Copy to clipboard";
|
||||
private Message? copiedMessage;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
@@ -222,9 +219,10 @@
|
||||
}
|
||||
finally
|
||||
{
|
||||
await EnsureMessageIsVisibleAsync();
|
||||
|
||||
isAsking = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await EnsureMessageIsVisibleAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,25 +230,29 @@
|
||||
{
|
||||
question = null;
|
||||
conversationId = Guid.NewGuid();
|
||||
copiedMessage = null;
|
||||
messages.Clear();
|
||||
}
|
||||
|
||||
private async Task CopyToClipboardAsync(string text)
|
||||
private async Task CopyToClipboardAsync(Message message)
|
||||
{
|
||||
if (text is null)
|
||||
if (string.IsNullOrEmpty(message.Text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
|
||||
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", message.Text);
|
||||
|
||||
showCopyConfirmation = true;
|
||||
toolTipText = "Copied!";
|
||||
copiedMessage = message;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await Task.Delay(3000); // Shows the checkmark for 3 seconds
|
||||
|
||||
toolTipText = "Copy to clipboard";
|
||||
showCopyConfirmation = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
if (copiedMessage == message)
|
||||
{
|
||||
copiedMessage = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatTokenUsage(TokenUsageResponse? tokenUsageResponse)
|
||||
|
||||
@@ -119,6 +119,29 @@ input[type="checkbox"] + label {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* The copy button only shows an icon, so the default button chrome is removed and just the pointer cursor is kept. */
|
||||
.copy-button ::deep .btn {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
|
||||
.copy-button ::deep .btn:hover {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
color: var(--bs-body-color);
|
||||
}
|
||||
|
||||
.copy-button ::deep .btn:focus,
|
||||
.copy-button ::deep .btn:focus-visible,
|
||||
.copy-button ::deep .btn:active {
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn-clipboard {
|
||||
line-height: 1;
|
||||
color: var(--bs-body-color);
|
||||
|
||||
Reference in New Issue
Block a user