Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 0 additions & 65 deletions .github/workflows/preview-pages.yml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
<div>
<h3 class="text-xl font-semibold mb-2 text-card-foreground">Drawer & Sheet</h3>
<div class="flex flex-wrap gap-2">
<Drawer @bind-Open="drawerOpen" Side="@DrawerSide.Bottom">
<Drawer @bind-Open="drawerOpen" Side="@DrawerSide.Bottom" Compositional="true">
<DrawerTrigger>
<Button Variant="@ButtonVariant.Outline">Open Drawer</Button>
</DrawerTrigger>
Expand All @@ -134,7 +134,7 @@
<Button @onclick="() => drawerOpen = false">Close</Button>
</DrawerContent>
</Drawer>
<Sheet @bind-Open="sheetOpen" Side="@SheetSide.Right">
<Sheet @bind-Open="sheetOpen" Side="@SheetSide.Right" Compositional="true">
<SheetTrigger>
<Button Variant="@ButtonVariant.Outline">Open Sheet</Button>
</SheetTrigger>
Expand Down
17 changes: 17 additions & 0 deletions NET10/BlazorInteractiveServer/Components/Models/DataTableModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,20 @@ public enum SortDirection
Ascending,
Descending
}

// Passed to OnDataRequest when DataTable is in ServerSide mode. The server-side data
// loader receives filter / sort / page state and returns just the current page.
public class DataTableRequest
{
public int Skip { get; set; }
public int Take { get; set; }
public string? SortBy { get; set; }
public SortDirection SortDirection { get; set; } = SortDirection.None;
public string SearchQuery { get; set; } = "";
}

public class DataTableResponse<TItem>
{
public IEnumerable<TItem> Items { get; set; } = Array.Empty<TItem>();
public int TotalCount { get; set; }
}
51 changes: 46 additions & 5 deletions NET10/BlazorInteractiveServer/Components/Services/SonnerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ public class SonnerToastItem
public string Message { get; set; } = "";
public string? Description { get; set; }
public string Variant { get; set; } = "default";
// Zero or negative disables auto-dismiss (toast stays until manually closed).
public TimeSpan Duration { get; set; } = TimeSpan.FromSeconds(4);
public DateTime CreatedAt { get; } = DateTime.UtcNow;
}

public interface ISonnerService
{
IReadOnlyList<SonnerToastItem> Toasts { get; }
void Show(string message, string? description = null, string variant = "default");
void Show(string message, string? description = null, string variant = "default", TimeSpan? duration = null);
void Success(string message, string? description = null, TimeSpan? duration = null);
void Error(string message, string? description = null, TimeSpan? duration = null);
void Info(string message, string? description = null, TimeSpan? duration = null);
void Warning(string message, string? description = null, TimeSpan? duration = null);
void Remove(string id);
}

public class SonnerService : ISonnerService
Expand All @@ -21,15 +28,49 @@ public class SonnerService : ISonnerService
public IReadOnlyList<SonnerToastItem> Toasts => _toasts;
public event Action? OnChange;

public void Show(string message, string? description = null, string variant = "default")
public void Show(string message, string? description = null, string variant = "default", TimeSpan? duration = null)
{
_toasts.Add(new SonnerToastItem { Message = message, Description = description, Variant = variant });
var toast = new SonnerToastItem
{
Message = message,
Description = description,
Variant = variant,
Duration = duration ?? TimeSpan.FromSeconds(4)
};
_toasts.Add(toast);
OnChange?.Invoke();

if (toast.Duration > TimeSpan.Zero)
{
_ = AutoDismissAsync(toast.Id, toast.Duration);
}
}

public void Success(string message, string? description = null, TimeSpan? duration = null)
=> Show(message, description, "success", duration);

public void Error(string message, string? description = null, TimeSpan? duration = null)
=> Show(message, description, "destructive", duration);

public void Info(string message, string? description = null, TimeSpan? duration = null)
=> Show(message, description, "info", duration);

public void Warning(string message, string? description = null, TimeSpan? duration = null)
=> Show(message, description, "warning", duration);

public void Remove(string id)
{
_toasts.RemoveAll(t => t.Id == id);
OnChange?.Invoke();
var removed = _toasts.RemoveAll(t => t.Id == id);
if (removed > 0) OnChange?.Invoke();
}

private async Task AutoDismissAsync(string id, TimeSpan delay)
{
try
{
await Task.Delay(delay);
Remove(id);
}
catch { }
}
}
75 changes: 65 additions & 10 deletions NET10/BlazorInteractiveServer/Components/UI/Combobox.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
@namespace BlazorInteractiveServer.Components.UI
@using Microsoft.JSInterop
@implements IAsyncDisposable
@inject IJSRuntime JS

<div class="@Shell.Cn("relative", Class)" @attributes="AdditionalAttributes">
<button type="button"
Expand Down Expand Up @@ -75,30 +78,82 @@
[Parameter] public bool Disabled { get; set; }
[Parameter] public IEnumerable<string>? Options { get; set; }
[Parameter] public bool Searchable { get; set; } = true;
/// Dismiss the dropdown when the page scrolls. Matches shadcn/Radix behavior.
/// Set false to keep the dropdown open across scroll (useful for infinite-scroll pages).
[Parameter] public bool CloseOnScroll { get; set; }
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public string? Class { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? AdditionalAttributes { get; set; }

private bool IsOpen { get; set; }
private string searchQuery = "";

private void Toggle()
private DotNetObjectReference<Combobox>? _selfRef;
private readonly string _dismissHandle = Guid.NewGuid().ToString("N");
private bool _dismissRegistered;

private async Task Toggle()
{
if (!Disabled)
if (Disabled) return;
IsOpen = !IsOpen;
if (IsOpen)
{
searchQuery = "";
await RegisterDismissAsync();
}
else
{
IsOpen = !IsOpen;
if (IsOpen) searchQuery = "";
await UnregisterDismissAsync();
}
}

private void Close() => IsOpen = false;


private async Task Close()
{
IsOpen = false;
await UnregisterDismissAsync();
}

[JSInvokable]
public async Task OnDismissEvent()
{
if (IsOpen)
{
IsOpen = false;
await UnregisterDismissAsync();
StateHasChanged();
}
}

private async Task RegisterDismissAsync()
{
if (!CloseOnScroll || _dismissRegistered) return;
_selfRef ??= DotNetObjectReference.Create(this);
try
{
await JS.InvokeVoidAsync("ShellUI.onDismissEvents", _dismissHandle, _selfRef);
_dismissRegistered = true;
}
catch { }
}

private async Task UnregisterDismissAsync()
{
if (!_dismissRegistered) return;
try { await JS.InvokeVoidAsync("ShellUI.offDismissEvents", _dismissHandle); } catch { }
_dismissRegistered = false;
}

public async Task SelectValue(string value)
{
Value = value;
await ValueChanged.InvokeAsync(value);
IsOpen = false;
await UnregisterDismissAsync();
await ValueChanged.InvokeAsync(value);
}
}

public async ValueTask DisposeAsync()
{
await UnregisterDismissAsync();
_selfRef?.Dispose();
}
}
2 changes: 1 addition & 1 deletion NET10/BlazorInteractiveServer/Components/UI/Command.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<DialogContent Class="overflow-hidden p-0">
<!-- Search Input -->
<div class="flex items-center border-b border-border px-3 py-2">
<svg class="mr-2 h-4 w-4 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<svg class="mr-2 h-4 w-4 text-foreground/70" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
<input
Expand Down
Loading
Loading