feat(ui): collapsible folders for the list sidebar

Folders render above the top-level lists, each with its own collapsible
children; tapping a folder header folds it instead of selecting it.
Lists move in by dragging onto a folder header (or onto a row inside it)
and out by dropping between top-level rows or via the context menu.

Creating a folder is a toggle in the New-list modal, not a third sidebar
button: it reduces the modal to name + icon and writes a folder row
instead of the list row the create flow pre-created.
This commit is contained in:
mika kuns
2026-09-10 08:29:56 +02:00
parent 13e860f06b
commit 25c9a619dc
9 changed files with 522 additions and 95 deletions
@@ -0,0 +1,23 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace ClaudeDo.Ui.ViewModels.Islands;
/// <summary>
/// A sidebar folder row. Not a <see cref="ListNavItemViewModel"/> and not a
/// <see cref="ListKind"/> — a folder holds no tasks and is never selectable, so tapping it
/// toggles <see cref="IsCollapsed"/> instead of changing the selection.
/// </summary>
public sealed partial class ListFolderNavItemViewModel : ViewModelBase
{
public required string Id { get; init; }
[ObservableProperty] private string _name = "";
[ObservableProperty] private string _iconKey = "Folder";
[ObservableProperty] private bool _isCollapsed;
// Sum of the child lists' counts — a collapsed folder still shows what's inside it.
[ObservableProperty] private int _count;
// Set while a dragged list row hovers over this folder header (move-into-folder target).
[ObservableProperty] private bool _isDropTarget;
public ObservableCollection<ListNavItemViewModel> Lists { get; } = new();
}