Refactor Home and Notes components to improve note display and pagination
This commit enhances the Home.razor and Notes.razor components by restructuring the note display logic. The Home component now correctly wraps the pinned notes section in a MudPaper component, ensuring consistent styling. The Notes component has been updated to utilize ClientSidePagination for better performance and user experience, replacing the previous pagination logic. This change simplifies the code and improves the overall maintainability of the note management interface.
This commit is contained in:
@@ -35,14 +35,13 @@
|
||||
@if (_pinnedNotes.Any())
|
||||
{
|
||||
<MudPaper Elevation="0" Class="mb-4">
|
||||
<MudText Typo="Typo.h4">Pinned Notes</MudText>
|
||||
</MudPaper>
|
||||
<MudGrid>
|
||||
@foreach (var note in _pinnedNotes)
|
||||
{
|
||||
<NoteCard Note="@note" OnNoteChanged="HandleNoteChanged" />
|
||||
}
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
}
|
||||
|
||||
@if (!_hasStudents)
|
||||
|
||||
@@ -39,124 +39,100 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudExpansionPanels MultiExpansion="true">
|
||||
@foreach (var note in _paginatedNotes)
|
||||
{
|
||||
var noteId = note.Id;
|
||||
var initialExpanded = _selectedNoteId.HasValue && noteId == _selectedNoteId.Value;
|
||||
if (!_expandedNotes.ContainsKey(noteId))
|
||||
{
|
||||
_expandedNotes[noteId] = initialExpanded;
|
||||
}
|
||||
var isExpanded = GetNoteExpanded(noteId);
|
||||
<MudExpansionPanel @key="@($"note-{noteId}")"
|
||||
Icon="@Icons.Material.Filled.Note"
|
||||
IsExpanded="@isExpanded"
|
||||
ExpandedChanged="@((bool expanded) => OnPanelExpandedChanged(noteId, expanded))">
|
||||
<TitleContent>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Class="flex-grow-1">
|
||||
<MudText Typo="Typo.body1" Class="flex-grow-1" Style="min-width: 0;">
|
||||
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;">
|
||||
@GetNoteHeaderText(note, isExpanded)
|
||||
</span>
|
||||
</MudText>
|
||||
@if (!note.Title.StartsWith("@") && !note.IsDeleted && !isExpanded)
|
||||
{
|
||||
<MudButton StartIcon="@(note.IsPinned ? Icons.Material.Filled.PushPin : Icons.Material.Outlined.PushPin)"
|
||||
OnClick="() => TogglePin(note)"
|
||||
OnClick:StopPropagation="true"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="@(note.IsPinned ? Color.Primary : Color.Default)"
|
||||
Disabled="@(IsPinDisabled(note))"
|
||||
Title="@(note.IsPinned ? "Unpin note" : "Pin note")"
|
||||
Class="flex-shrink-0" />
|
||||
}
|
||||
</MudStack>
|
||||
</TitleContent>
|
||||
<ChildContent>
|
||||
<MudStack Spacing="2">
|
||||
@if (!string.IsNullOrWhiteSpace(note.Content))
|
||||
<ClientSidePagination T="Note" Items="@_notes" DefaultPageSize="@DefaultPageSize">
|
||||
<ChildContent Context="paginatedNotes">
|
||||
<MudExpansionPanels MultiExpansion="true">
|
||||
@foreach (var note in paginatedNotes)
|
||||
{
|
||||
var noteId = note.Id;
|
||||
var initialExpanded = _selectedNoteId.HasValue && noteId == _selectedNoteId.Value;
|
||||
if (!_expandedNotes.ContainsKey(noteId))
|
||||
{
|
||||
<MudPaper Elevation="0" Class="pa-3" Style="background-color: var(--mud-palette-background-grey);">
|
||||
<div class="markdown-content">
|
||||
@((MarkupString)MarkdownHelper.ToHtml(note.Content))
|
||||
</div>
|
||||
</MudPaper>
|
||||
_expandedNotes[noteId] = initialExpanded;
|
||||
}
|
||||
<MudStack Row="true" Spacing="2" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
Last updated: @note.UpdatedAt.ToString("g") by @(note.LastModifiedBy ?? "Unknown")
|
||||
</MudText>
|
||||
<MudStack Row="true" Spacing="2">
|
||||
@if (!note.IsDeleted)
|
||||
var isExpanded = GetNoteExpanded(noteId);
|
||||
<MudExpansionPanel @key="@($"note-{noteId}")"
|
||||
Icon="@Icons.Material.Filled.Note"
|
||||
IsExpanded="@isExpanded"
|
||||
ExpandedChanged="@((bool expanded) => OnPanelExpandedChanged(noteId, expanded))">
|
||||
<TitleContent>
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2" Class="flex-grow-1">
|
||||
<MudText Typo="Typo.body1" Class="flex-grow-1" Style="min-width: 0;">
|
||||
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;">
|
||||
@GetNoteHeaderText(note, isExpanded)
|
||||
</span>
|
||||
</MudText>
|
||||
@if (!note.Title.StartsWith("@") && !note.IsDeleted && !isExpanded)
|
||||
{
|
||||
<MudButton StartIcon="@(note.IsPinned ? Icons.Material.Filled.PushPin : Icons.Material.Outlined.PushPin)"
|
||||
OnClick="() => TogglePin(note)"
|
||||
OnClick:StopPropagation="true"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="@(note.IsPinned ? Color.Primary : Color.Default)"
|
||||
Disabled="@(IsPinDisabled(note))"
|
||||
Title="@(note.IsPinned ? "Unpin note" : "Pin note")"
|
||||
Class="flex-shrink-0" />
|
||||
}
|
||||
</MudStack>
|
||||
</TitleContent>
|
||||
<ChildContent>
|
||||
<MudStack Spacing="2">
|
||||
@if (!string.IsNullOrWhiteSpace(note.Content))
|
||||
{
|
||||
<MudButton StartIcon="@Icons.Material.Filled.History"
|
||||
OnClick="() => OpenHistoryDialog(note.Id)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small">
|
||||
History
|
||||
</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Edit"
|
||||
OnClick="() => OpenEditDialog(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Outlined.Delete"
|
||||
OnClick="() => DeleteNote(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Error">
|
||||
Delete
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Restore"
|
||||
OnClick="() => RestoreNote(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Success">
|
||||
Restore
|
||||
</MudButton>
|
||||
<MudPaper Elevation="0" Class="pa-3" Style="background-color: var(--mud-palette-background-grey);">
|
||||
<div class="markdown-content">
|
||||
@((MarkupString)MarkdownHelper.ToHtml(note.Content))
|
||||
</div>
|
||||
</MudPaper>
|
||||
}
|
||||
<MudStack Row="true" Spacing="2" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
Last updated: @note.UpdatedAt.ToString("g") by @(note.LastModifiedBy ?? "Unknown")
|
||||
</MudText>
|
||||
<MudStack Row="true" Spacing="2">
|
||||
@if (!note.IsDeleted)
|
||||
{
|
||||
<MudButton StartIcon="@Icons.Material.Filled.History"
|
||||
OnClick="() => OpenHistoryDialog(note.Id)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small">
|
||||
History
|
||||
</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Edit"
|
||||
OnClick="() => OpenEditDialog(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary">
|
||||
Edit
|
||||
</MudButton>
|
||||
<MudButton StartIcon="@Icons.Material.Outlined.Delete"
|
||||
OnClick="() => DeleteNote(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Error">
|
||||
Delete
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton StartIcon="@Icons.Material.Filled.Restore"
|
||||
OnClick="() => RestoreNote(note)"
|
||||
Variant="Variant.Text"
|
||||
Size="Size.Small"
|
||||
Color="Color.Success">
|
||||
Restore
|
||||
</MudButton>
|
||||
}
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
</ChildContent>
|
||||
</MudExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
|
||||
@if (_totalPages > 1 || _currentPageSize != DefaultPageSize)
|
||||
{
|
||||
<MudStack Row="true" Spacing="2" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mt-4">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudText Typo="Typo.body2">Items per page:</MudText>
|
||||
<MudSelect T="int" @bind-Value="CurrentPageSize" Variant="Variant.Outlined" Dense="true" Style="width: 100px;">
|
||||
<MudSelectItem Value="10">10</MudSelectItem>
|
||||
<MudSelectItem Value="25">25</MudSelectItem>
|
||||
<MudSelectItem Value="50">50</MudSelectItem>
|
||||
<MudSelectItem Value="100">100</MudSelectItem>
|
||||
</MudSelect>
|
||||
</MudStack>
|
||||
|
||||
@if (_totalPages > 1)
|
||||
{
|
||||
<MudPagination Count="@_totalPages"
|
||||
Selected="@(_currentPage + 1)"
|
||||
SelectedChanged="OnPageChanged"
|
||||
ShowFirstButton="true"
|
||||
ShowLastButton="true" />
|
||||
}
|
||||
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
Showing @_displayStart to @_displayEnd of @_totalNotes
|
||||
</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
</ChildContent>
|
||||
</MudExpansionPanel>
|
||||
}
|
||||
</MudExpansionPanels>
|
||||
</ChildContent>
|
||||
</ClientSidePagination>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@@ -167,31 +143,9 @@
|
||||
private const int DefaultPageSize = 25;
|
||||
|
||||
private List<Note> _notes = [];
|
||||
private List<Note> _paginatedNotes = [];
|
||||
private bool _isLoading = true;
|
||||
private bool _showRemoved = false;
|
||||
private int _currentPage = 0;
|
||||
private int _currentPageSize = DefaultPageSize;
|
||||
private int _pinnedCount = 0;
|
||||
|
||||
private int CurrentPageSize
|
||||
{
|
||||
get => _currentPageSize;
|
||||
set
|
||||
{
|
||||
if (_currentPageSize != value)
|
||||
{
|
||||
_currentPageSize = value;
|
||||
_currentPage = 0; // Reset to first page when page size changes
|
||||
UpdatePagination();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
private int _totalNotes = 0;
|
||||
private int _totalPages = 0;
|
||||
private int _displayStart = 0;
|
||||
private int _displayEnd = 0;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private bool _isDisposed = false;
|
||||
private int? _selectedNoteId;
|
||||
@@ -271,8 +225,6 @@
|
||||
{
|
||||
_expandedNotes.Remove(id);
|
||||
}
|
||||
|
||||
UpdatePagination();
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
@@ -321,39 +273,6 @@
|
||||
return MarkdownHelper.StripMarkdownPreview(content, MaxPreviewLength);
|
||||
}
|
||||
|
||||
private void UpdatePagination()
|
||||
{
|
||||
_totalNotes = _notes.Count;
|
||||
_totalPages = (int)Math.Ceiling((double)_totalNotes / _currentPageSize);
|
||||
|
||||
// Ensure current page is valid
|
||||
if (_currentPage >= _totalPages && _totalPages > 0)
|
||||
{
|
||||
_currentPage = _totalPages - 1;
|
||||
}
|
||||
else if (_currentPage < 0)
|
||||
{
|
||||
_currentPage = 0;
|
||||
}
|
||||
|
||||
// Calculate paginated notes
|
||||
var startIndex = _currentPage * _currentPageSize;
|
||||
_paginatedNotes = _notes.Skip(startIndex).Take(_currentPageSize).ToList();
|
||||
|
||||
// Calculate display range
|
||||
_displayStart = _totalNotes > 0 ? startIndex + 1 : 0;
|
||||
_displayEnd = Math.Min(startIndex + _currentPageSize, _totalNotes);
|
||||
}
|
||||
|
||||
private void OnPageChanged(int newPage)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
_currentPage = newPage - 1; // MudPagination is 1-based, we use 0-based
|
||||
UpdatePagination();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ToggleRemovedFilter()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
@namespace WebApp.Components.Shared.Components
|
||||
@typeparam T
|
||||
@using MudBlazor
|
||||
|
||||
@ChildContent(_paginatedItems)
|
||||
|
||||
@if (_totalPages > 1 || _currentPageSize != DefaultPageSize)
|
||||
{
|
||||
<MudStack Row="true" Spacing="2" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="mt-4">
|
||||
@if (ShowPageSizeSelector)
|
||||
{
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudText Typo="Typo.body2">Items per page:</MudText>
|
||||
<MudSelect T="int" Value="@_currentPageSize" ValueChanged="OnPageSizeChanged" Variant="Variant.Outlined" Dense="true" Style="width: 100px;">
|
||||
@foreach (var option in PageSizeOptions)
|
||||
{
|
||||
<MudSelectItem Value="@option">@option</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@if (_totalPages > 1)
|
||||
{
|
||||
<MudPagination Count="@_totalPages"
|
||||
Selected="@(_currentPage + 1)"
|
||||
SelectedChanged="OnPageChanged"
|
||||
ShowFirstButton="true"
|
||||
ShowLastButton="true" />
|
||||
}
|
||||
|
||||
@if (ShowItemCount)
|
||||
{
|
||||
<MudText Typo="Typo.body2" Color="Color.Secondary">
|
||||
Showing @_displayStart to @_displayEnd of @_totalItems
|
||||
</MudText>
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<T> _paginatedItems = new();
|
||||
private int _currentPage = 0;
|
||||
private int _currentPageSize;
|
||||
private int _totalItems = 0;
|
||||
private int _totalPages = 0;
|
||||
private int _displayStart = 0;
|
||||
private int _displayEnd = 0;
|
||||
private IEnumerable<T>? _previousItems;
|
||||
|
||||
[Parameter] public IEnumerable<T> Items { get; set; } = Enumerable.Empty<T>();
|
||||
[Parameter] public int DefaultPageSize { get; set; } = 25;
|
||||
[Parameter] public int[] PageSizeOptions { get; set; } = new[] { 10, 25, 50, 100 };
|
||||
[Parameter] public bool ShowPageSizeSelector { get; set; } = true;
|
||||
[Parameter] public bool ShowItemCount { get; set; } = true;
|
||||
[Parameter] public RenderFragment<IEnumerable<T>> ChildContent { get; set; } = null!;
|
||||
|
||||
public IEnumerable<T> PaginatedItems => _paginatedItems;
|
||||
public int CurrentPage => _currentPage;
|
||||
public int CurrentPageSize => _currentPageSize;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_currentPageSize = DefaultPageSize;
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
// Check if items collection has changed (reference or count)
|
||||
var itemsChanged = _previousItems != Items ||
|
||||
(_previousItems?.Count() ?? 0) != (Items?.Count() ?? 0);
|
||||
|
||||
if (itemsChanged)
|
||||
{
|
||||
_previousItems = Items?.ToList();
|
||||
_currentPage = 0; // Reset to first page when items change
|
||||
}
|
||||
|
||||
UpdatePagination();
|
||||
}
|
||||
|
||||
private void OnPageSizeChanged(int newPageSize)
|
||||
{
|
||||
if (_currentPageSize != newPageSize)
|
||||
{
|
||||
_currentPageSize = newPageSize;
|
||||
_currentPage = 0; // Reset to first page when page size changes
|
||||
UpdatePagination();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePagination()
|
||||
{
|
||||
var itemsList = Items?.ToList() ?? new List<T>();
|
||||
_totalItems = itemsList.Count;
|
||||
_totalPages = _totalItems > 0 ? (int)Math.Ceiling((double)_totalItems / _currentPageSize) : 0;
|
||||
|
||||
// Ensure current page is valid
|
||||
if (_totalPages > 0 && _currentPage >= _totalPages)
|
||||
{
|
||||
_currentPage = _totalPages - 1;
|
||||
}
|
||||
else if (_currentPage < 0)
|
||||
{
|
||||
_currentPage = 0;
|
||||
}
|
||||
|
||||
// Calculate paginated items
|
||||
var startIndex = _currentPage * _currentPageSize;
|
||||
_paginatedItems = itemsList.Skip(startIndex).Take(_currentPageSize).ToList();
|
||||
|
||||
// Calculate display range
|
||||
_displayStart = _totalItems > 0 ? startIndex + 1 : 0;
|
||||
_displayEnd = Math.Min(startIndex + _currentPageSize, _totalItems);
|
||||
}
|
||||
|
||||
private void OnPageChanged(int newPage)
|
||||
{
|
||||
_currentPage = newPage - 1; // MudPagination is 1-based, we use 0-based
|
||||
UpdatePagination();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user