feat(ui): Listen und Ordner in einer gemischten Sidebar-Reihenfolge

Die oberste Sidebar-Ebene ist jetzt eine gemischte TopLevel-Collection, so
dass eine Liste auch oberhalb eines Ordners stehen kann. DropAsync deckt
alle vier Quelle/Ziel-Kombinationen ab, ReorderSidebarAsync numeriert die
gesamte sichtbare Reihenfolge ueber lists und list_folders hinweg.
This commit is contained in:
mika kuns
2026-09-10 15:40:10 +02:00
parent 1770b67713
commit bfb1c6523c
9 changed files with 472 additions and 191 deletions
@@ -5,10 +5,13 @@
xmlns:loc="using:ClaudeDo.Ui.Localization"
x:Class="ClaudeDo.Ui.Views.Islands.ListsIslandView"
x:DataType="vm:ListsIslandViewModel">
<UserControl.Resources>
<!-- One user-list row. Shared by the top-level list and by each folder's children, so the
<!-- Implicit, type-matched templates: the sidebar's top level is one mixed ItemsControl over
TopLevel (folders and folder-less lists interleaved), and each folder's children resolve the
list template the same way. -->
<UserControl.DataTemplates>
<!-- One user-list row. Shared by the top level and by each folder's children, so the
row (context menu, drag handlers, drop indicators) exists once. -->
<DataTemplate x:Key="UserListRow" DataType="vm:ListNavItemViewModel">
<DataTemplate DataType="vm:ListNavItemViewModel">
<Grid RowDefinitions="Auto,Auto,Auto">
<!-- Above-row drop indicator -->
@@ -88,7 +91,65 @@
IsVisible="{Binding DropHintBelow}"/>
</Grid>
</DataTemplate>
</UserControl.Resources>
<!-- One folder: header (never selectable — tapping folds it) plus its child lists. The two
drop indicators sit around the whole block, so "below this folder" doesn't draw a line
between the header and its children. -->
<DataTemplate DataType="vm:ListFolderNavItemViewModel">
<Grid RowDefinitions="Auto,Auto,Auto">
<Border Grid.Row="0" Height="2" VerticalAlignment="Center" Margin="4,0"
Background="{DynamicResource MossBrush}" CornerRadius="1"
IsVisible="{Binding DropHintAbove}"/>
<StackPanel Grid.Row="1">
<Border Classes="list-item"
Classes.drop-target="{Binding IsDropTarget}"
Tapped="OnFolderTapped"
DragDrop.AllowDrop="True"
DragDrop.DragOver="OnFolderDragOver"
DragDrop.Drop="OnFolderDrop">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="{loc:Tr lists.contextFolderSettings}"
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenFolderSettingsCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</Border.ContextMenu>
<Grid ColumnDefinitions="12,20,*,Auto">
<PathIcon Grid.Column="0" Classes="list-icon"
Width="9" Height="9"
Data="{StaticResource Icon.ChevronRight}"
IsVisible="{Binding IsCollapsed}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<PathIcon Grid.Column="0" Classes="list-icon"
Width="9" Height="9"
Data="{StaticResource Icon.ChevronDown}"
IsVisible="{Binding !IsCollapsed}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<PathIcon Grid.Column="1" Classes="list-icon"
Width="14" Height="14"
Data="{Binding IconKey, Converter={StaticResource IconKey}}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Classes="list-label"
Text="{Binding Name}"
VerticalAlignment="Center" Margin="8,0"
Foreground="{DynamicResource TextDimBrush}" FontSize="{StaticResource FontSizeBody}"/>
<TextBlock Grid.Column="3" Classes="list-count"
Text="{Binding Count}"/>
</Grid>
</Border>
<ItemsControl ItemsSource="{Binding Lists}"
IsVisible="{Binding !IsCollapsed}"
Margin="14,0,0,0"/>
</StackPanel>
<Border Grid.Row="2" Height="2" VerticalAlignment="Center" Margin="4,0"
Background="{DynamicResource MossBrush}" CornerRadius="1"
IsVisible="{Binding DropHintBelow}"/>
</Grid>
</DataTemplate>
</UserControl.DataTemplates>
<DockPanel LastChildFill="True">
@@ -218,60 +279,9 @@
<PathIcon Data="{StaticResource Icon.Folder}" Width="14" Height="14"/>
</Button>
</Grid>
<!-- Folders first, each with its own (collapsible) child lists, then the top-level lists.
Folders are never selectable — tapping the header folds it. -->
<ItemsControl ItemsSource="{Binding Folders}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:ListFolderNavItemViewModel">
<StackPanel>
<Border Classes="list-item"
Classes.drop-target="{Binding IsDropTarget}"
Tapped="OnFolderTapped"
DragDrop.AllowDrop="True"
DragDrop.DragOver="OnFolderDragOver"
DragDrop.Drop="OnFolderDrop">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="{loc:Tr lists.contextFolderSettings}"
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenFolderSettingsCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</Border.ContextMenu>
<Grid ColumnDefinitions="12,20,*,Auto">
<PathIcon Grid.Column="0" Classes="list-icon"
Width="9" Height="9"
Data="{StaticResource Icon.ChevronRight}"
IsVisible="{Binding IsCollapsed}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<PathIcon Grid.Column="0" Classes="list-icon"
Width="9" Height="9"
Data="{StaticResource Icon.ChevronDown}"
IsVisible="{Binding !IsCollapsed}"
HorizontalAlignment="Left" VerticalAlignment="Center"/>
<PathIcon Grid.Column="1" Classes="list-icon"
Width="14" Height="14"
Data="{Binding IconKey, Converter={StaticResource IconKey}}"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Classes="list-label"
Text="{Binding Name}"
VerticalAlignment="Center" Margin="8,0"
Foreground="{DynamicResource TextDimBrush}" FontSize="{StaticResource FontSizeBody}"/>
<TextBlock Grid.Column="3" Classes="list-count"
Text="{Binding Count}"/>
</Grid>
</Border>
<ItemsControl ItemsSource="{Binding Lists}"
IsVisible="{Binding !IsCollapsed}"
Margin="14,0,0,0"
ItemTemplate="{StaticResource UserListRow}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding RootLists}"
ItemTemplate="{StaticResource UserListRow}"/>
<!-- Folders and folder-less lists in one sequence, so either can be dragged above the
other; each item picks its template from UserControl.DataTemplates by type. -->
<ItemsControl ItemsSource="{Binding TopLevel}"/>
</StackPanel>
</ScrollViewer>