<UserControl xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:a="https://aioros.dev" xmlns:m="using:Aioros.Site.Demos.Kanban" xmlns:b="using:Aioros.Site.Demos.Kanban"
x:DataType="m:KanbanModel">
<UserControl.Resources>
<b:PriorityBrush x:Key="PriorityBrush" />
<b:DueText x:Key="DueText" />
<b:IsLate x:Key="IsLate" />
<b:Initials x:Key="Initials" />
<b:Counted x:Key="Counted" />
<ControlTheme x:Key="CardAction" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="6,4" />
<Setter Property="CornerRadius" Value="6" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource CosmoMutedForegroundBrush}" />
</ControlTheme>
</UserControl.Resources>
<UserControl.Styles>
<Style Selector="Border.lane">
<Setter Property="Background" Value="{DynamicResource CosmoMutedBrush}" />
<Setter Property="CornerRadius" Value="14" />
<Setter Property="Padding" Value="12" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Style Selector="^.over-limit">
<Setter Property="BorderBrush" Value="#E5484D" />
</Style>
</Style>
<Style Selector="Border.lane.over-limit TextBlock.count">
<Setter Property="Foreground" Value="#E5484D" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Border.card">
<Setter Property="Background" Value="{DynamicResource CosmoCardBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource CosmoBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Padding" Value="12" />
<Setter Property="RenderTransform" Value="translate(0px, 0px)" />
<Setter Property="Transitions">
<Transitions>
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:0.18" Easing="CubicEaseOut" />
<BrushTransition Property="BorderBrush" Duration="0:0:0.18" />
</Transitions>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="BorderBrush" Value="{DynamicResource CosmoPrimaryBrush}" />
<Setter Property="RenderTransform" Value="translate(0px, -2px)" />
</Style>
<Style Selector="^.blocked">
<Setter Property="Background" Value="{DynamicResource CosmoAccentBrush}" />
<Setter Property="BorderBrush" Value="#E5484D" />
</Style>
</Style>
<Style Selector="TextBlock.wrap">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style Selector="Border.card.blocked TextBlock.title">
<Setter Property="Foreground" Value="{DynamicResource CosmoMutedForegroundBrush}" />
</Style>
<Style Selector="Border.card.late TextBlock.due">
<Setter Property="Foreground" Value="#E5484D" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
<Style Selector="Border.card:not(.late) TextBlock.due">
<Setter Property="Foreground" Value="{DynamicResource CosmoMutedForegroundBrush}" />
</Style>
<Style Selector="TextBlock.done-note">
<Setter Property="IsVisible" Value="False" />
<Setter Property="Foreground" Value="#30A46C" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
<Style Selector="Border.lane.done TextBlock.done-note">
<Setter Property="IsVisible" Value="True" />
</Style>
<Style Selector="Border.lane.done TextBlock.due">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="Border.badge-blocked">
<Setter Property="Background" Value="#E5484D" />
<Setter Property="CornerRadius" Value="999" />
<Setter Property="Padding" Value="8,2" />
<Style.Animations>
<Animation Duration="0:0:1.2" IterationCount="Infinite" PlaybackDirection="Alternate">
<KeyFrame Cue="0%"><Setter Property="Opacity" Value="1" /></KeyFrame>
<KeyFrame Cue="100%"><Setter Property="Opacity" Value="0.55" /></KeyFrame>
</Animation>
</Style.Animations>
</Style>
<Style Selector="Border.kind">
<Setter Property="CornerRadius" Value="6" />
<Setter Property="Padding" Value="6,1" />
</Style>
<Style Selector="Border.kind.task"><Setter Property="Background" Value="#E6F4FE" /></Style>
<Style Selector="Border.kind.task TextBlock"><Setter Property="Foreground" Value="#0D74CE" /></Style>
<Style Selector="Border.kind.bug"><Setter Property="Background" Value="#FFEBEC" /></Style>
<Style Selector="Border.kind.bug TextBlock"><Setter Property="Foreground" Value="#CE2C31" /></Style>
<Style Selector="Border.kind.epic"><Setter Property="Background" Value="#F2EEFF" /></Style>
<Style Selector="Border.kind.epic TextBlock"><Setter Property="Foreground" Value="#6550B9" /></Style>
<Style Selector="Border.avatar">
<Setter Property="Width" Value="26" />
<Setter Property="Height" Value="26" />
<Setter Property="CornerRadius" Value="13" />
<Setter Property="Background" Value="{DynamicResource KanbanAccentSoft}" />
</Style>
<Style Selector="Border.avatar TextBlock">
<Setter Property="FontSize" Value="11" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="{DynamicResource CosmoPrimaryBrush}" />
</Style>
</UserControl.Styles>
<StackPanel Spacing="20">
<StackPanel Spacing="4">
<TextBlock Classes="h1" Text="Kanban" />
<TextBlock Classes="lead" Text="Plain XAML, no code-behind: each kind of card has its own DataTemplate, converters turn priorities into colors and dates into words, multi-bindings count stories and work in progress, bound classes mark blocked and late cards, the buttons reach the page's commands through $parent bindings, run on the server, and cards drag between lanes with the a:Drag behavior (or the keyboard: Space, the arrows, Space)." />
</StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding Lanes}" MinWidth="1040">
<ItemsControl.ItemsPanel><ItemsPanelTemplate><UniformGrid Columns="4" ColumnSpacing="14" /></ItemsPanelTemplate></ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="m:LaneView">
<Border Classes="lane" Classes.over-limit="{Binding IsOverLimit}" Classes.done="{Binding IsDone}">
<StackPanel Spacing="10">
<Grid ColumnDefinitions="*,Auto" Margin="4,2,4,4">
<TextBlock Text="{Binding Title}" FontWeight="SemiBold" VerticalAlignment="Center" />
<Border Grid.Column="1" Classes="chip">
<Panel>
<TextBlock Classes="small count" IsVisible="{Binding HasLimit}">
<TextBlock.Text><MultiBinding StringFormat="{}{0} / {1}"><Binding Path="Count" /><Binding Path="Limit" /></MultiBinding></TextBlock.Text>
</TextBlock>
<TextBlock Classes="small count" Text="{Binding Count}" IsVisible="{Binding !HasLimit}" />
</Panel>
</Border>
</Grid>
<TextBlock Classes="small" Text="Over the work-in-progress limit" Foreground="#E5484D" IsVisible="{Binding IsOverLimit}" Margin="4,0" />
<ItemsControl ItemsSource="{Binding Cards}" a:Drag.Target="{Binding Key}" a:Drag.Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).PlaceCommand}" AutomationProperties.Name="{Binding Title}">
<ItemsControl.ItemsPanel><ItemsPanelTemplate><StackPanel Spacing="10" MinHeight="48" /></ItemsPanelTemplate></ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate x:DataType="m:TaskCard">
<Border Classes="card" Classes.blocked="{Binding Blocked}" Classes.late="{Binding Due, Converter={StaticResource IsLate}}" a:Drag.Item="{Binding Id}" AutomationProperties.Name="{Binding Title}">
<StackPanel Spacing="8">
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
<Border Width="8" Height="8" CornerRadius="4" VerticalAlignment="Center" Background="{Binding Priority, Converter={StaticResource PriorityBrush}}" />
<Border Grid.Column="1" Classes="kind task"><TextBlock Classes="small" Text="Task" FontWeight="SemiBold" /></Border>
<TextBlock Grid.Column="2" Classes="muted small" Text="{Binding Id}" VerticalAlignment="Center" />
<Border Grid.Column="3" Classes="badge-blocked" IsVisible="{Binding Blocked}"><TextBlock Classes="small" Text="Blocked" Foreground="#FFFFFF" FontWeight="SemiBold" /></Border>
</Grid>
<TextBlock Classes="title wrap" Text="{Binding Title}" FontWeight="SemiBold" />
<Border Classes="chip" HorizontalAlignment="Left"><TextBlock Classes="small" Text="{Binding Area}" /></Border>
<Grid ColumnDefinitions="*,Auto">
<TextBlock Classes="small due" Text="{Binding Due, Converter={StaticResource DueText}}" VerticalAlignment="Center" />
<TextBlock Classes="small done-note" Text="Completed" VerticalAlignment="Center" />
<Border Grid.Column="1" Classes="avatar" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Text="{Binding Owner, Converter={StaticResource Initials}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<TextBlock Grid.Column="1" Classes="muted small" Text="Unassigned" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNull}}" VerticalAlignment="Center" />
</Grid>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveLeftCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveLeft}" AutomationProperties.Name="Move left"><a:Icon Kind="chevron-left" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveRightCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveRight}" AutomationProperties.Name="Move right"><a:Icon Kind="chevron-right" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).BlockCommand}" CommandParameter="{Binding Id}" AutomationProperties.Name="Block or unblock"><a:Icon Kind="lock" Size="15" /></Button>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:DataType="m:BugCard">
<Border Classes="card" Classes.blocked="{Binding Blocked}" Classes.late="{Binding Due, Converter={StaticResource IsLate}}" a:Drag.Item="{Binding Id}" AutomationProperties.Name="{Binding Title}">
<StackPanel Spacing="8">
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
<Border Width="8" Height="8" CornerRadius="4" VerticalAlignment="Center" Background="{Binding Priority, Converter={StaticResource PriorityBrush}}" />
<Border Grid.Column="1" Classes="kind bug"><TextBlock Classes="small" Text="Bug" FontWeight="SemiBold" /></Border>
<TextBlock Grid.Column="2" Classes="muted small" Text="{Binding Id}" VerticalAlignment="Center" />
<Border Grid.Column="3" Classes="badge-blocked" IsVisible="{Binding Blocked}"><TextBlock Classes="small" Text="Blocked" Foreground="#FFFFFF" FontWeight="SemiBold" /></Border>
</Grid>
<TextBlock Classes="title wrap" Text="{Binding Title}" FontWeight="SemiBold" />
<StackPanel Orientation="Horizontal" Spacing="4">
<TextBlock Classes="muted small" Text="{Binding Reports, Converter={StaticResource Counted}, ConverterParameter=report}" />
<TextBlock Classes="muted small" Text="{Binding Environment, StringFormat='· {0}'}" />
</StackPanel>
<Grid ColumnDefinitions="*,Auto">
<TextBlock Classes="small due" Text="{Binding Due, Converter={StaticResource DueText}}" VerticalAlignment="Center" />
<TextBlock Classes="small done-note" Text="Completed" VerticalAlignment="Center" />
<Border Grid.Column="1" Classes="avatar" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Text="{Binding Owner, Converter={StaticResource Initials}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<TextBlock Grid.Column="1" Classes="muted small" Text="Unassigned" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNull}}" VerticalAlignment="Center" />
</Grid>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveLeftCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveLeft}" AutomationProperties.Name="Move left"><a:Icon Kind="chevron-left" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveRightCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveRight}" AutomationProperties.Name="Move right"><a:Icon Kind="chevron-right" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).BlockCommand}" CommandParameter="{Binding Id}" AutomationProperties.Name="Block or unblock"><a:Icon Kind="lock" Size="15" /></Button>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate x:DataType="m:EpicCard">
<Border Classes="card" Classes.blocked="{Binding Blocked}" Classes.late="{Binding Due, Converter={StaticResource IsLate}}" a:Drag.Item="{Binding Id}" AutomationProperties.Name="{Binding Title}">
<StackPanel Spacing="8">
<Grid ColumnDefinitions="Auto,Auto,*,Auto" ColumnSpacing="6">
<Border Width="8" Height="8" CornerRadius="4" VerticalAlignment="Center" Background="{Binding Priority, Converter={StaticResource PriorityBrush}}" />
<Border Grid.Column="1" Classes="kind epic"><TextBlock Classes="small" Text="Epic" FontWeight="SemiBold" /></Border>
<TextBlock Grid.Column="2" Classes="muted small" Text="{Binding Id}" VerticalAlignment="Center" />
<Border Grid.Column="3" Classes="badge-blocked" IsVisible="{Binding Blocked}"><TextBlock Classes="small" Text="Blocked" Foreground="#FFFFFF" FontWeight="SemiBold" /></Border>
</Grid>
<TextBlock Classes="title wrap" Text="{Binding Title}" FontWeight="SemiBold" />
<ProgressBar Value="{Binding Percent}" Maximum="100" Height="6" MinHeight="6" />
<TextBlock Classes="muted small">
<TextBlock.Text><MultiBinding StringFormat="{}{0} of {1} stories done"><Binding Path="Done" /><Binding Path="Total" /></MultiBinding></TextBlock.Text>
</TextBlock>
<Grid ColumnDefinitions="*,Auto">
<TextBlock Classes="small due" Text="{Binding Due, Converter={StaticResource DueText}}" VerticalAlignment="Center" />
<TextBlock Classes="small done-note" Text="Completed" VerticalAlignment="Center" />
<Border Grid.Column="1" Classes="avatar" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNotNull}}">
<TextBlock Text="{Binding Owner, Converter={StaticResource Initials}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<TextBlock Grid.Column="1" Classes="muted small" Text="Unassigned" IsVisible="{Binding Owner, Converter={x:Static ObjectConverters.IsNull}}" VerticalAlignment="Center" />
</Grid>
<StackPanel Orientation="Horizontal" Spacing="2">
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveLeftCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveLeft}" AutomationProperties.Name="Move left"><a:Icon Kind="chevron-left" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).MoveRightCommand}" CommandParameter="{Binding Id}" IsVisible="{Binding CanMoveRight}" AutomationProperties.Name="Move right"><a:Icon Kind="chevron-right" Size="16" /></Button>
<Button Theme="{StaticResource CardAction}" Command="{Binding $parent[UserControl].((m:KanbanModel)DataContext).BlockCommand}" CommandParameter="{Binding Id}" AutomationProperties.Name="Block or unblock"><a:Icon Kind="lock" Size="15" /></Button>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
<TextBlock Classes="muted small" Text="No cards" IsVisible="{Binding IsEmpty}" HorizontalAlignment="Center" Margin="0,8" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Expander Classes="source" Header="View the XAML of this page">
<Border Classes="code"><SelectableTextBlock Classes="source" Text="{Binding Xaml}" /></Border>
</Expander>
</StackPanel>
</UserControl>