Initial commit: Tasks.Module
This commit is contained in:
@@ -0,0 +1,537 @@
|
||||
@using MudBlazor
|
||||
@using Tasks.Module.Models
|
||||
@using Tasks.Module.Services
|
||||
|
||||
<MudStack Orientation="Orientation.Vertical" Spacing="0" Style="height: 100%;">
|
||||
<MudStack Row="true" Justify="Justify.FlexStart" AlignItems="AlignItems.Center" Class="pa-2">
|
||||
<MudText Typo="Typo.caption" Color="Color.Default" Class="ml-2">TASKS SHEETS</MudText>
|
||||
<MudSpacer />
|
||||
<MudStack Row="true" Spacing="1">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.CreateNewFolder" Size="Size.Small" Color="Color.Inherit" OnClick="@OnCreateFolder" Title="New Folder" />
|
||||
<MudIconButton Icon="@Icons.Material.Filled.NoteAdd" Size="Size.Small" Color="Color.Inherit" OnClick="@OnCreateFile" Title="New Tasks Sheet" />
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
<div style="flex: 1; overflow-y: auto; min-height: 0;"
|
||||
class="@(_rootDragOver ? "root-drop-zone-active" : "")"
|
||||
@ondragenter="@OnRootDragEnter"
|
||||
@ondragenter:preventDefault="true"
|
||||
@ondragover="@OnRootDragOver"
|
||||
@ondragover:preventDefault="true"
|
||||
@ondragleave="@OnRootDragLeave"
|
||||
@ondrop="@OnRootDrop"
|
||||
@ondrop:preventDefault="true">
|
||||
@if (_loading)
|
||||
{
|
||||
<MudProgressLinear Indeterminate="true" Class="my-2" />
|
||||
}
|
||||
else if (_treeItems.Any())
|
||||
{
|
||||
<MudStack Spacing="0" Class="pa-2">
|
||||
@foreach (var item in _treeItems)
|
||||
{
|
||||
<FolderTreeItem Item="@item"
|
||||
OnItemClick="@OnItemClick"
|
||||
OnItemExpand="@OnItemExpand"
|
||||
OnContextMenuWithArgs="@OnContextMenuWithArgs"
|
||||
OnDragStart="@OnDragStartHandler"
|
||||
OnDragEnd="@OnDragEndHandler"
|
||||
OnItemDrop="@OnItemDropHandler" />
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText Class="pa-4" Typo="Typo.body2" Color="Color.Secondary" Align="Align.Center">
|
||||
No tasks sheets yet
|
||||
</MudText>
|
||||
}
|
||||
</div>
|
||||
</MudStack>
|
||||
|
||||
<style>
|
||||
.root-drop-zone-active {
|
||||
background-color: var(--mud-palette-primary-hover);
|
||||
outline: 2px dashed var(--mud-palette-primary);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@if (_contextMenuOpen)
|
||||
{
|
||||
<div Style="@GetContextMenuStyle()" @onclick:stopPropagation="true">
|
||||
<MudPaper Elevation="4" Class="pa-2" Style="min-width: 160px;">
|
||||
<MudStack Spacing="0">
|
||||
<MudButton FullWidth="true" StartIcon="@Icons.Material.Filled.CreateNewFolder" OnClick="@CreateSubFolder" Disabled="@(_contextItemType != TreeItemType.Folder)">
|
||||
New Subfolder
|
||||
</MudButton>
|
||||
<MudButton FullWidth="true" StartIcon="@Icons.Material.Filled.NoteAdd" OnClick="@CreateSubFile" Disabled="@(_contextItemType != TreeItemType.Folder)">
|
||||
New Task List
|
||||
</MudButton>
|
||||
<MudDivider Class="my-1" />
|
||||
<MudButton FullWidth="true" StartIcon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="@DeleteSelected" Disabled="@(_contextItemId == null)">
|
||||
Delete
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
</div>
|
||||
<div style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000;" @onclick="CloseContextMenu" @oncontextmenu:preventDefault="true"></div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private bool _loading = true;
|
||||
private List<TreeItemDto> _treeItems = new();
|
||||
private bool _contextMenuOpen = false;
|
||||
private string? _contextItemId = null;
|
||||
private TreeItemType _contextItemType = TreeItemType.Folder;
|
||||
private double _contextMenuX = 0;
|
||||
private double _contextMenuY = 0;
|
||||
private bool _rootDragOver = false;
|
||||
private TreeItemDto? _draggingItem = null;
|
||||
private bool _isMoving = false;
|
||||
private int _dragSessionId = 0;
|
||||
|
||||
[Inject] private IFolderService FolderService { get; set; } = null!;
|
||||
[Inject] private ISnackbar Snackbar { get; set; } = null!;
|
||||
[Inject] private IDialogService DialogService { get; set; } = null!;
|
||||
[Inject] private NavigationManager NavigationManager { get; set; } = null!;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await RefreshTree();
|
||||
}
|
||||
|
||||
private async Task RefreshTree()
|
||||
{
|
||||
_loading = true;
|
||||
try
|
||||
{
|
||||
var newTreeItems = new List<TreeItemDto>();
|
||||
|
||||
var rootFolders = await FolderService.GetAllRootFoldersAsync();
|
||||
foreach (var folder in rootFolders)
|
||||
{
|
||||
var treeItem = await BuildTreeItem(folder);
|
||||
newTreeItems.Add(treeItem);
|
||||
}
|
||||
|
||||
var rootTasksSheets = await FolderService.GetRootTasksSheetsAsync();
|
||||
foreach (var ts in rootTasksSheets)
|
||||
{
|
||||
newTreeItems.Add(new TreeItemDto
|
||||
{
|
||||
Id = ts.Id,
|
||||
Name = ts.Title,
|
||||
Type = TreeItemType.TasksSheet,
|
||||
ParentId = null,
|
||||
IsExpanded = false,
|
||||
Children = new List<TreeItemDto>()
|
||||
});
|
||||
}
|
||||
|
||||
_treeItems = newTreeItems;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<TreeItemDto> BuildTreeItem(FolderDto folder)
|
||||
{
|
||||
var item = new TreeItemDto
|
||||
{
|
||||
Id = folder.Id,
|
||||
Name = folder.Name,
|
||||
Type = TreeItemType.Folder,
|
||||
ParentId = folder.ParentId,
|
||||
IsExpanded = false,
|
||||
Children = new List<TreeItemDto>()
|
||||
};
|
||||
|
||||
foreach (var child in folder.Children)
|
||||
{
|
||||
var childItem = await BuildTreeItem(child);
|
||||
item.Children.Add(childItem);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var tasksSheets = await FolderService.GetTasksSheetsInFolderAsync(folder.Id);
|
||||
foreach (var ts in tasksSheets)
|
||||
{
|
||||
item.Children.Add(new TreeItemDto
|
||||
{
|
||||
Id = ts.Id,
|
||||
Name = ts.Title,
|
||||
Type = TreeItemType.TasksSheet,
|
||||
ParentId = folder.Id,
|
||||
IsExpanded = false,
|
||||
Children = new List<TreeItemDto>()
|
||||
});
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private async Task OnItemClick(TreeItemDto item)
|
||||
{
|
||||
if (item.Type == TreeItemType.Folder)
|
||||
{
|
||||
item.IsExpanded = !item.IsExpanded;
|
||||
if (item.IsExpanded && !item.Children.Any())
|
||||
{
|
||||
await LoadFolderChildren(item);
|
||||
}
|
||||
}
|
||||
else if (item.Type == TreeItemType.TasksSheet)
|
||||
{
|
||||
NavigationManager.NavigateTo($"/tasks/taskssheet/{item.Id}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnItemExpand(TreeItemDto item)
|
||||
{
|
||||
if (item.Type != TreeItemType.Folder) return;
|
||||
|
||||
item.IsExpanded = !item.IsExpanded;
|
||||
|
||||
if (item.IsExpanded && !item.Children.Any())
|
||||
{
|
||||
await LoadFolderChildren(item);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadFolderChildren(TreeItemDto item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var folder = await FolderService.GetFolderByIdAsync(item.Id);
|
||||
if (folder != null)
|
||||
{
|
||||
item.Children.Clear();
|
||||
foreach (var child in folder.Children)
|
||||
{
|
||||
var childItem = await BuildTreeItem(child);
|
||||
item.Children.Add(childItem);
|
||||
}
|
||||
|
||||
var tasksSheets = await FolderService.GetTasksSheetsInFolderAsync(item.Id);
|
||||
foreach (var ts in tasksSheets)
|
||||
{
|
||||
item.Children.Add(new TreeItemDto
|
||||
{
|
||||
Id = ts.Id,
|
||||
Name = ts.Title,
|
||||
Type = TreeItemType.TasksSheet,
|
||||
ParentId = item.Id,
|
||||
IsExpanded = false,
|
||||
Children = new List<TreeItemDto>()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContextMenuWithArgs((TreeItemDto, MouseEventArgs) args)
|
||||
{
|
||||
_contextItemId = args.Item1.Id;
|
||||
_contextItemType = args.Item1.Type;
|
||||
_contextMenuX = args.Item2.ClientX;
|
||||
_contextMenuY = args.Item2.ClientY;
|
||||
_contextMenuOpen = true;
|
||||
}
|
||||
|
||||
private void CloseContextMenu()
|
||||
{
|
||||
_contextMenuOpen = false;
|
||||
_contextItemId = null;
|
||||
}
|
||||
|
||||
private string GetContextMenuStyle()
|
||||
{
|
||||
return $"position: fixed; left: {_contextMenuX}px; top: {_contextMenuY}px; z-index: 1100;";
|
||||
}
|
||||
|
||||
private async Task OnCreateFolder()
|
||||
{
|
||||
var parameters = new DialogParameters<InputDialog>
|
||||
{
|
||||
{ "Title", "New Folder" },
|
||||
{ "Label", "Folder Name" }
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<InputDialog>("New Folder", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
await FolderService.CreateFolderAsync(new CreateFolderDto { Name = name });
|
||||
Snackbar.Add($"Created folder '{name}'", Severity.Success);
|
||||
await RefreshTree();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error creating folder: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnCreateFile()
|
||||
{
|
||||
var parameters = new DialogParameters<InputDialog>
|
||||
{
|
||||
{ "Title", "New Tasks Sheet" },
|
||||
{ "Label", "Tasks Sheet Name" }
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<InputDialog>("New Tasks Sheet", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
await FolderService.CreateTasksSheetAsync(new CreateTasksSheetDto { Title = name });
|
||||
Snackbar.Add($"Created tasks sheet '{name}'", Severity.Success);
|
||||
await RefreshTree();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error creating tasks sheet: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateSubFolder()
|
||||
{
|
||||
if (_contextItemId == null) return;
|
||||
var parentId = _contextItemId;
|
||||
CloseContextMenu();
|
||||
|
||||
var parameters = new DialogParameters<InputDialog>
|
||||
{
|
||||
{ "Title", "New Subfolder" },
|
||||
{ "Label", "Folder Name" }
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<InputDialog>("New Subfolder", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
await FolderService.CreateFolderAsync(new CreateFolderDto { Name = name, ParentId = parentId });
|
||||
Snackbar.Add($"Created subfolder '{name}'", Severity.Success);
|
||||
await RefreshTree();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateSubFile()
|
||||
{
|
||||
if (_contextItemId == null) return;
|
||||
var parentId = _contextItemId;
|
||||
CloseContextMenu();
|
||||
|
||||
var parameters = new DialogParameters<InputDialog>
|
||||
{
|
||||
{ "Title", "New Tasks Sheet" },
|
||||
{ "Label", "Tasks Sheet Name" }
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<InputDialog>("New Tasks Sheet", parameters);
|
||||
var result = await dialog.Result;
|
||||
|
||||
if (!result.Canceled && result.Data is string name && !string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
try
|
||||
{
|
||||
await FolderService.CreateTasksSheetAsync(new CreateTasksSheetDto { Title = name, ParentFolderId = parentId });
|
||||
Snackbar.Add($"Created tasks sheet '{name}'", Severity.Success);
|
||||
await RefreshTree();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteSelected()
|
||||
{
|
||||
if (_contextItemId == null) return;
|
||||
var itemId = _contextItemId;
|
||||
var itemType = _contextItemType;
|
||||
CloseContextMenu();
|
||||
|
||||
try
|
||||
{
|
||||
if (itemType == TreeItemType.Folder)
|
||||
{
|
||||
await FolderService.DeleteFolderAsync(itemId);
|
||||
Snackbar.Add("Folder deleted", Severity.Success);
|
||||
}
|
||||
await RefreshTree();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragStartHandler(TreeItemDto item)
|
||||
{
|
||||
_dragSessionId++;
|
||||
_draggingItem = item;
|
||||
item.IsDragSource = true;
|
||||
}
|
||||
|
||||
private void OnDragEndHandler(TreeItemDto item)
|
||||
{
|
||||
_dragSessionId++;
|
||||
_isMoving = false;
|
||||
ClearDragState(_treeItems);
|
||||
_rootDragOver = false;
|
||||
_draggingItem = null;
|
||||
}
|
||||
|
||||
private void ClearDragState(List<TreeItemDto> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
item.IsDragSource = false;
|
||||
item.IsDragOver = false;
|
||||
if (item.Children.Any())
|
||||
ClearDragState(item.Children);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnItemDropHandler(TreeItemDto target)
|
||||
{
|
||||
var source = _draggingItem;
|
||||
if (source == null) return;
|
||||
|
||||
var sessionId = _dragSessionId;
|
||||
await MoveItem(source, target.Id, sessionId);
|
||||
_draggingItem = null;
|
||||
}
|
||||
|
||||
private void OnRootDragEnter()
|
||||
{
|
||||
if (_draggingItem != null) _rootDragOver = true;
|
||||
}
|
||||
|
||||
private void OnRootDragOver()
|
||||
{
|
||||
if (_draggingItem != null) _rootDragOver = true;
|
||||
}
|
||||
|
||||
private void OnRootDragLeave()
|
||||
{
|
||||
_rootDragOver = false;
|
||||
}
|
||||
|
||||
private async Task OnRootDrop()
|
||||
{
|
||||
_rootDragOver = false;
|
||||
var source = _draggingItem;
|
||||
if (source == null) return;
|
||||
|
||||
var sessionId = _dragSessionId;
|
||||
await MoveItem(source, null, sessionId);
|
||||
_draggingItem = null;
|
||||
}
|
||||
|
||||
private async Task MoveItem(TreeItemDto source, string? targetParentId, int sessionId)
|
||||
{
|
||||
if (_isMoving || sessionId != _dragSessionId) return;
|
||||
_isMoving = true;
|
||||
|
||||
if (source.Id == targetParentId) { _isMoving = false; return; }
|
||||
if (source.ParentId == targetParentId && targetParentId != null) { _isMoving = false; return; }
|
||||
|
||||
if (source.Type == TreeItemType.Folder && targetParentId != null)
|
||||
{
|
||||
if (await IsDescendantOf(source.Id, targetParentId))
|
||||
{
|
||||
Snackbar.Add("Cannot move a folder into one of its children.", Severity.Error);
|
||||
_isMoving = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool success;
|
||||
if (source.Type == TreeItemType.Folder)
|
||||
{
|
||||
success = await FolderService.MoveFolderAsync(source.Id, targetParentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = await FolderService.MoveTasksSheetAsync(source.Id, targetParentId);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
Snackbar.Add($"Moved '{source.Name}'", Severity.Success);
|
||||
await RefreshTree();
|
||||
}
|
||||
else
|
||||
{
|
||||
Snackbar.Add($"Could not move '{source.Name}' — item not found.", Severity.Warning);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Snackbar.Add($"Error moving item: {ex.Message}", Severity.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isMoving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> IsDescendantOf(string ancestorId, string candidateId)
|
||||
{
|
||||
var queue = new Queue<string>();
|
||||
queue.Enqueue(ancestorId);
|
||||
var visited = new HashSet<string>();
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var current = queue.Dequeue();
|
||||
if (!visited.Add(current)) continue;
|
||||
|
||||
if (current == candidateId) return true;
|
||||
|
||||
var children = await FolderService.GetFolderByIdAsync(current);
|
||||
if (children == null) continue;
|
||||
|
||||
foreach (var child in children.Children)
|
||||
{
|
||||
queue.Enqueue(child.Id);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
@using MudBlazor
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using Tasks.Module.Models
|
||||
|
||||
<div class="folder-item @(Item.Type == TreeItemType.Folder ? "drop-zone" : "") @(Item.IsDragOver ? "drop-zone-active" : "") @(Item.IsDragSource ? "drag-source" : "")"
|
||||
draggable="true"
|
||||
@onclick="@(() => OnClick(Item))"
|
||||
@oncontextmenu:preventDefault="true"
|
||||
@oncontextmenu="@OnContextMenuHandler"
|
||||
@ondragstart="@OnDragStartLocal"
|
||||
@ondragend="@OnDragEndLocal"
|
||||
@ondragenter="@OnDragEnter"
|
||||
@ondragenter:preventDefault="true"
|
||||
@ondragover="@OnDragOver"
|
||||
@ondragover:preventDefault="true"
|
||||
@ondragleave="@OnDragLeave"
|
||||
@ondrop="@OnDrop"
|
||||
@ondrop:preventDefault="true"
|
||||
style="width: 100%; cursor: pointer;">
|
||||
<MudStack Row="true" Spacing="0" AlignItems="AlignItems.Center" Style="width: 100%; padding: 4px 8px; border-radius: 4px;" Class="folder-item-row">
|
||||
@if (Item.Type == TreeItemType.Folder)
|
||||
{
|
||||
<MudIcon Icon="@(Item.IsExpanded ? Icons.Material.Filled.KeyboardArrowDown : Icons.Material.Filled.KeyboardArrowRight)"
|
||||
Size="Size.Small"
|
||||
Class="mr-1"
|
||||
Style="width: 20px;" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div style="width: 20px;" class="mr-1"></div>
|
||||
}
|
||||
|
||||
<MudIcon Icon="@(Item.Type == TreeItemType.Folder
|
||||
? (Item.IsExpanded ? Icons.Material.Filled.FolderOpen : Icons.Material.Filled.Folder)
|
||||
: Icons.Material.Filled.Description)"
|
||||
Size="Size.Small"
|
||||
Class="mr-2"
|
||||
Color="@(Item.Type == TreeItemType.Folder ? Color.Warning : Color.Primary)"
|
||||
Style="width: 20px;" />
|
||||
|
||||
<MudText Typo="Typo.body2" Class="flex-grow-1">
|
||||
@Item.Name
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</div>
|
||||
|
||||
@if (Item.IsExpanded && Item.Children.Any())
|
||||
{
|
||||
<MudStack Spacing="0" Class="pl-4" Style="border-left: 1px solid var(--mud-palette-lines-default);">
|
||||
@foreach (var child in Item.Children)
|
||||
{
|
||||
<FolderTreeItem Item="@child"
|
||||
OnItemClick="@OnItemClick"
|
||||
OnItemExpand="@OnItemExpand"
|
||||
OnContextMenuWithArgs="@OnContextMenuWithArgs"
|
||||
OnDragStart="@OnDragStartRecursive"
|
||||
OnDragEnd="@OnDragEndRecursive"
|
||||
OnItemDrop="@OnItemDropRecursive" />
|
||||
}
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
<style>
|
||||
.folder-item:hover .folder-item-row {
|
||||
background-color: var(--mud-palette-action-default-hover);
|
||||
}
|
||||
|
||||
.folder-item.drag-source .folder-item-row {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.folder-item.drop-zone {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.folder-item.drop-zone-active > .folder-item-row {
|
||||
background-color: var(--mud-palette-primary-hover);
|
||||
outline: 2px dashed var(--mud-palette-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public TreeItemDto Item { get; set; } = null!;
|
||||
[Parameter] public EventCallback<TreeItemDto> OnItemClick { get; set; }
|
||||
[Parameter] public EventCallback<TreeItemDto> OnItemExpand { get; set; }
|
||||
[Parameter] public EventCallback<(TreeItemDto, MouseEventArgs)> OnContextMenuWithArgs { get; set; }
|
||||
[Parameter] public EventCallback<TreeItemDto> OnDragStart { get; set; }
|
||||
[Parameter] public EventCallback<TreeItemDto> OnDragEnd { get; set; }
|
||||
[Parameter] public EventCallback<TreeItemDto> OnItemDrop { get; set; }
|
||||
|
||||
private Task OnClick(TreeItemDto item)
|
||||
{
|
||||
return OnItemClick.InvokeAsync(item);
|
||||
}
|
||||
|
||||
private async Task OnContextMenuHandler(MouseEventArgs args)
|
||||
{
|
||||
await OnContextMenuWithArgs.InvokeAsync((Item, args));
|
||||
}
|
||||
|
||||
private async Task OnDragStartRecursive(TreeItemDto item)
|
||||
{
|
||||
await OnDragStart.InvokeAsync(item);
|
||||
}
|
||||
|
||||
private async Task OnDragEndRecursive()
|
||||
{
|
||||
await OnDragEnd.InvokeAsync(Item);
|
||||
}
|
||||
|
||||
private async Task OnItemDropRecursive(TreeItemDto item)
|
||||
{
|
||||
await OnItemDrop.InvokeAsync(item);
|
||||
}
|
||||
|
||||
private async Task OnDragStartLocal()
|
||||
{
|
||||
await OnDragStart.InvokeAsync(Item);
|
||||
}
|
||||
|
||||
private async Task OnDragEndLocal()
|
||||
{
|
||||
await OnDragEnd.InvokeAsync(Item);
|
||||
}
|
||||
|
||||
private void OnDragEnter()
|
||||
{
|
||||
if (Item.Type == TreeItemType.Folder)
|
||||
{
|
||||
Item.IsDragOver = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragOver(DragEventArgs args)
|
||||
{
|
||||
if (Item.Type == TreeItemType.Folder)
|
||||
{
|
||||
Item.IsDragOver = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDragLeave()
|
||||
{
|
||||
Item.IsDragOver = false;
|
||||
}
|
||||
|
||||
private async Task OnDrop()
|
||||
{
|
||||
Item.IsDragOver = false;
|
||||
await OnItemDrop.InvokeAsync(Item);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user