first commit
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
# SbTreeView
|
||||
|
||||
A hierarchical tree view component for displaying nested data structures.
|
||||
|
||||
## Type Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| TItem | The type of items in the tree |
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| Items | IEnumerable\<TItem\> | Empty | Root items |
|
||||
| ChildSelector | Func\<TItem, IEnumerable\<TItem\>\> | Returns empty | Function to get child items |
|
||||
| TextSelector | Func\<TItem, string\> | ToString() | Function to get item display text |
|
||||
| IconSelector | Func\<TItem, string?\>? | null | Function to get item icon name |
|
||||
| IsExpandedSelector | Func\<TItem, bool\>? | null | Function to check if item is expanded |
|
||||
| IsSelectedSelector | Func\<TItem, bool\>? | null | Function to check if item is selected |
|
||||
| SelectedItem | TItem? | null | Currently selected item |
|
||||
| ShowCheckboxes | bool | false | Whether to show checkboxes |
|
||||
| CheckedItems | HashSet\<TItem\> | new() | Set of checked items |
|
||||
| Class | string? | null | Additional CSS classes |
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| SelectedItemChanged | EventCallback\<TItem\> | Fired when item is selected |
|
||||
| OnToggle | EventCallback\<TItem\> | Fired when item is expanded/collapsed |
|
||||
| CheckedItemsChanged | EventCallback\<HashSet\<TItem\>\> | Fired when checked items change |
|
||||
|
||||
## CSS Classes
|
||||
|
||||
- `sb-treeview` - Base class
|
||||
|
||||
## Accessibility
|
||||
|
||||
- Uses `role="tree"` for the container
|
||||
- Uses `role="treeitem"` for items
|
||||
- Uses `role="group"` for child containers
|
||||
- Uses `aria-level` for nesting depth
|
||||
- Uses `aria-expanded` for expandable items
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="TreeNode"
|
||||
Items="@rootNodes"
|
||||
ChildSelector="@(n => n.Children)"
|
||||
TextSelector="@(n => n.Name)" />
|
||||
|
||||
@code {
|
||||
private List<TreeNode> rootNodes = new()
|
||||
{
|
||||
new TreeNode("Documents", new[]
|
||||
{
|
||||
new TreeNode("Work"),
|
||||
new TreeNode("Personal")
|
||||
}),
|
||||
new TreeNode("Pictures"),
|
||||
new TreeNode("Music")
|
||||
};
|
||||
|
||||
public class TreeNode
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<TreeNode> Children { get; set; } = new();
|
||||
|
||||
public TreeNode(string name, IEnumerable<TreeNode>? children = null)
|
||||
{
|
||||
Name = name;
|
||||
Children = children?.ToList() ?? new();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Icons
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="FileNode"
|
||||
Items="@files"
|
||||
ChildSelector="@(n => n.Children)"
|
||||
TextSelector="@(n => n.Name)"
|
||||
IconSelector="@(n => n.IsFolder ? "folder" : "file")" />
|
||||
|
||||
@code {
|
||||
private List<FileNode> files = new()
|
||||
{
|
||||
new FileNode("src", isFolder: true, children: new[]
|
||||
{
|
||||
new FileNode("components", isFolder: true),
|
||||
new FileNode("App.razor"),
|
||||
new FileNode("Program.cs")
|
||||
}),
|
||||
new FileNode("README.md")
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Selectable Tree
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="Category"
|
||||
Items="@categories"
|
||||
ChildSelector="@(c => c.SubCategories)"
|
||||
TextSelector="@(c => c.Name)"
|
||||
@bind-SelectedItem="selectedCategory" />
|
||||
|
||||
<SbText>Selected: @(selectedCategory?.Name ?? "None")</SbText>
|
||||
|
||||
@code {
|
||||
private Category? selectedCategory;
|
||||
private List<Category> categories = /* ... */;
|
||||
}
|
||||
```
|
||||
|
||||
### With Checkboxes
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="Permission"
|
||||
Items="@permissions"
|
||||
ChildSelector="@(p => p.Children)"
|
||||
TextSelector="@(p => p.Name)"
|
||||
ShowCheckboxes="true"
|
||||
@bind-CheckedItems="checkedPermissions" />
|
||||
|
||||
<SbButton OnClick="SavePermissions">
|
||||
Save (@checkedPermissions.Count selected)
|
||||
</SbButton>
|
||||
|
||||
@code {
|
||||
private HashSet<Permission> checkedPermissions = new();
|
||||
private List<Permission> permissions = new()
|
||||
{
|
||||
new Permission("Users", new[]
|
||||
{
|
||||
new Permission("View"),
|
||||
new Permission("Create"),
|
||||
new Permission("Edit"),
|
||||
new Permission("Delete")
|
||||
}),
|
||||
new Permission("Reports", new[]
|
||||
{
|
||||
new Permission("View"),
|
||||
new Permission("Export")
|
||||
})
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Controlled Expansion
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="TreeNode"
|
||||
Items="@nodes"
|
||||
ChildSelector="@(n => n.Children)"
|
||||
TextSelector="@(n => n.Name)"
|
||||
IsExpandedSelector="@(n => expandedNodes.Contains(n.Id))"
|
||||
OnToggle="HandleToggle" />
|
||||
|
||||
@code {
|
||||
private HashSet<int> expandedNodes = new() { 1, 2 };
|
||||
|
||||
private void HandleToggle(TreeNode node)
|
||||
{
|
||||
if (expandedNodes.Contains(node.Id))
|
||||
expandedNodes.Remove(node.Id);
|
||||
else
|
||||
expandedNodes.Add(node.Id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### File Explorer
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="FileSystemItem"
|
||||
Items="@fileSystem"
|
||||
ChildSelector="@(f => f.Children)"
|
||||
TextSelector="@(f => f.Name)"
|
||||
IconSelector="@GetIcon"
|
||||
@bind-SelectedItem="selectedFile"
|
||||
OnToggle="LoadChildren" />
|
||||
|
||||
@code {
|
||||
private List<FileSystemItem> fileSystem;
|
||||
private FileSystemItem? selectedFile;
|
||||
|
||||
private string? GetIcon(FileSystemItem item) => item.Type switch
|
||||
{
|
||||
FileType.Folder => "folder",
|
||||
FileType.File => GetFileIcon(item.Extension),
|
||||
_ => null
|
||||
};
|
||||
|
||||
private string GetFileIcon(string? ext) => ext switch
|
||||
{
|
||||
".cs" => "file-code",
|
||||
".json" => "file-json",
|
||||
".md" => "file-text",
|
||||
".jpg" or ".png" => "image",
|
||||
_ => "file"
|
||||
};
|
||||
|
||||
private async Task LoadChildren(FileSystemItem folder)
|
||||
{
|
||||
if (folder.Type == FileType.Folder && !folder.ChildrenLoaded)
|
||||
{
|
||||
folder.Children = await LoadFromDisk(folder.Path);
|
||||
folder.ChildrenLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Organization Hierarchy
|
||||
|
||||
```razor
|
||||
<SbTreeView TItem="Employee"
|
||||
Items="@executives"
|
||||
ChildSelector="@(e => e.DirectReports)"
|
||||
TextSelector="@(e => $"{e.Name} - {e.Title}")"
|
||||
IconSelector="@(_ => "user")"
|
||||
@bind-SelectedItem="selectedEmployee" />
|
||||
|
||||
@if (selectedEmployee != null)
|
||||
{
|
||||
<SbCard Class="mt-4">
|
||||
<SbStack Gap="2">
|
||||
<SbHeading Level="3">@selectedEmployee.Name</SbHeading>
|
||||
<SbText Color="muted">@selectedEmployee.Title</SbText>
|
||||
<SbText>@selectedEmployee.Email</SbText>
|
||||
<SbText>Direct Reports: @selectedEmployee.DirectReports.Count</SbText>
|
||||
</SbStack>
|
||||
</SbCard>
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user