Updates to FormItem label and A11y (#395)
* feat: 1. add AccessKey support for form item. 2. Support label positioning for different content height. 3. polish demo. * feat: add a transparent background for label.
This commit is contained in:
@@ -13,59 +13,42 @@
|
||||
x:DataType="vm:FormDemoViewModel"
|
||||
mc:Ignorable="d">
|
||||
<ScrollViewer>
|
||||
<StackPanel>
|
||||
<Grid RowDefinitions="Auto, Auto" ColumnDefinitions="Auto, Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Label Position" VerticalAlignment="Center"></TextBlock>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Label Alignment" VerticalAlignment="Center"></TextBlock>
|
||||
<u:EnumSelector Grid.Row="0" Grid.Column="1"
|
||||
Name="position"
|
||||
EnumType="common:Position"
|
||||
Value="{x:Static common:Position.Top}" />
|
||||
<u:EnumSelector Grid.Row="1" Grid.Column="1"
|
||||
Name="alignment"
|
||||
EnumType="HorizontalAlignment"
|
||||
Value="{x:Static HorizontalAlignment.Left}" />
|
||||
</Grid>
|
||||
<u:Form
|
||||
DataContext="{Binding Model}"
|
||||
LabelAlignment="{Binding #alignment.Value}"
|
||||
LabelPosition="{Binding #position.Value}"
|
||||
LabelWidth="*">
|
||||
<u:FormGroup Header="Information">
|
||||
<TextBox
|
||||
Width="300"
|
||||
u:FormItem.IsRequired="True"
|
||||
u:FormItem.Label="Name"
|
||||
Text="{Binding Name}" />
|
||||
<TextBox
|
||||
Width="300"
|
||||
u:FormItem.Label="Email"
|
||||
Text="{Binding Email}" />
|
||||
<u:NumericDoubleUpDown Value="{Binding Number}" u:FormItem.Label="Number" Width="300"/>
|
||||
<StackPanel HorizontalAlignment="Left" Margin="24 0 0 0 ">
|
||||
<u:Divider Content="Simple Form"/>
|
||||
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||
<TextBox Width="300" u:FormItem.Label="Message" Classes="TextArea"/>
|
||||
</u:Form>
|
||||
<u:Divider Content="Form accessibility"/>
|
||||
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||
<TextBox Width="300" u:FormItem.Label="_Name" />
|
||||
<TextBox Width="300" u:FormItem.Label="_Email" />
|
||||
<TextBox Width="300" u:FormItem.Label="_Message" Classes="TextArea"/>
|
||||
</u:Form>
|
||||
<u:Divider Content="Label on top"/>
|
||||
<u:Form LabelPosition="Top" LabelWidth="*">
|
||||
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||
</u:Form>
|
||||
<u:Divider Content="FormGroup"/>
|
||||
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||
<u:FormGroup Header="Basic Information">
|
||||
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||
</u:FormGroup>
|
||||
<u:FormItem Label="Please select a Date">
|
||||
<CalendarDatePicker SelectedDate="{Binding Date}" />
|
||||
</u:FormItem>
|
||||
|
||||
<u:FormGroup Header="Education">
|
||||
<TextBox
|
||||
HorizontalAlignment="Stretch"
|
||||
u:FormItem.IsRequired="True"
|
||||
u:FormItem.Label="Name"
|
||||
Text="{Binding Name}" />
|
||||
<TextBox
|
||||
HorizontalAlignment="Stretch"
|
||||
u:FormItem.Label="Email"
|
||||
u:FormItem.IsRequired="True"
|
||||
Text="{Binding Email}" />
|
||||
<u:FormGroup Header="Education Information">
|
||||
<TextBox Width="300" u:FormItem.Label="Collage" />
|
||||
<u:FormItem Label="Study Time">
|
||||
<u:DateRangePicker Width="300"></u:DateRangePicker>
|
||||
</u:FormItem>
|
||||
</u:FormGroup>
|
||||
<u:FormItem Label="Click to Submit">
|
||||
<Button Content="Button With Label" />
|
||||
</u:FormItem>
|
||||
<CheckBox Content="I Agree User Agreement" u:FormItem.NoLabel="True"></CheckBox>
|
||||
<u:FormItem NoLabel="True">
|
||||
<Button HorizontalAlignment="Left" Content="No Label" />
|
||||
</u:FormItem>
|
||||
<Button Content="Submit" Theme="{DynamicResource SolidButton}" u:FormItem.NoLabel="True" HorizontalAlignment="Stretch"/>
|
||||
</u:Form>
|
||||
<u:Divider Content="MVVM setup and validation" />
|
||||
<u:Form LabelPosition="Left" LabelWidth="*" DataContext="{Binding Model}">
|
||||
<TextBox Width="300" u:FormItem.Label="Name" Text="{Binding Name}"/>
|
||||
<TextBox Width="300" u:FormItem.Label="Email" Text="{Binding Email}" />
|
||||
</u:Form>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<ResourceDictionary
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:u="https://irihi.tech/ursa">
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
xmlns:converters="clr-namespace:Ursa.Themes.Semi.Converters">
|
||||
<!-- Add Resources Here -->
|
||||
<ControlTheme x:Key="{x:Type u:Form}" TargetType="u:Form">
|
||||
<Setter Property="Grid.IsSharedSizeScope" Value="False" />
|
||||
<ControlTheme x:Key="{x:Type u:Form}"
|
||||
TargetType="u:Form">
|
||||
<Setter Property="Grid.IsSharedSizeScope"
|
||||
Value="False" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:Form">
|
||||
<DataValidationErrors>
|
||||
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
<ItemsPresenter
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</DataValidationErrors>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
<Style Selector="^:fixed-width">
|
||||
<Setter Property="Grid.IsSharedSizeScope" Value="True" />
|
||||
<Setter Property="Grid.IsSharedSizeScope"
|
||||
Value="True" />
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:FormGroup}" TargetType="u:FormGroup">
|
||||
<ControlTheme x:Key="{x:Type u:FormGroup}"
|
||||
TargetType="u:FormGroup">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:FormGroup">
|
||||
<StackPanel>
|
||||
@@ -32,15 +38,17 @@
|
||||
Height="1"
|
||||
Margin="0,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
IsVisible="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={x:Static ObjectConverters.IsNotNull}}"
|
||||
Fill="{DynamicResource SemiColorBorder}" />
|
||||
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
Fill="{DynamicResource SemiColorBorder}"
|
||||
IsVisible="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<ItemsPresenter
|
||||
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</ControlTheme>
|
||||
|
||||
<ControlTheme x:Key="{x:Type u:FormItem}" TargetType="u:FormItem">
|
||||
<ControlTheme x:Key="{x:Type u:FormItem}"
|
||||
TargetType="u:FormItem">
|
||||
<Setter Property="Margin" Value="0 8" />
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:FormItem">
|
||||
@@ -50,13 +58,19 @@
|
||||
Margin="0,0,0,4"
|
||||
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
||||
Orientation="Horizontal">
|
||||
<ContentPresenter Content="{TemplateBinding Label}" FontWeight="{DynamicResource TextBlockTitleFontWeight}" />
|
||||
<Label
|
||||
Content="{TemplateBinding Label}"
|
||||
Background="Transparent"
|
||||
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
||||
Target="{Binding #PART_ContentPresenter.Content}" />
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SemiRed6}"
|
||||
IsVisible="{TemplateBinding IsRequired}"
|
||||
Text="*" />
|
||||
</StackPanel>
|
||||
<ContentPresenter Content="{TemplateBinding Content}" />
|
||||
<ContentPresenter
|
||||
Name="PART_ContentPresenter"
|
||||
Content="{TemplateBinding Content}" />
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
@@ -65,7 +79,8 @@
|
||||
<ControlTemplate TargetType="u:FormItem">
|
||||
<Grid RowDefinitions="*, *">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
||||
<ColumnDefinition Width="Auto"
|
||||
SharedSizeGroup="Label" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
@@ -74,10 +89,16 @@
|
||||
Width="{TemplateBinding LabelWidth}">
|
||||
<StackPanel
|
||||
Name="PART_LabelPanel"
|
||||
Margin="8,8,8,0"
|
||||
Margin="{Binding #PART_Label.Bounds.Height, Converter={x:Static converters:FormContentHeightToMarginConverter.Instance}}"
|
||||
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
||||
VerticalAlignment="{Binding #PART_ContentPresenter.Bounds.Height, Converter={x:Static converters:FormContentHeightToAlignmentConverter.Instance}}"
|
||||
Orientation="Horizontal">
|
||||
<ContentPresenter Content="{TemplateBinding Label}" FontWeight="{DynamicResource TextBlockTitleFontWeight}" />
|
||||
<Label
|
||||
Name="PART_Label"
|
||||
Content="{TemplateBinding Label}"
|
||||
Background="Transparent"
|
||||
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
||||
Target="{Binding #PART_ContentPresenter.Content}" />
|
||||
<TextBlock
|
||||
Foreground="{DynamicResource SemiRed6}"
|
||||
IsVisible="{TemplateBinding IsRequired}"
|
||||
@@ -85,8 +106,11 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<ContentPresenter
|
||||
Name="PART_ContentPresenter"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="{TemplateBinding Content}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
@@ -95,9 +119,10 @@
|
||||
<Style Selector="^:no-label">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate TargetType="u:FormItem">
|
||||
<ContentPresenter Content="{TemplateBinding Content}" />
|
||||
<ContentPresenter
|
||||
Content="{TemplateBinding Content}" />
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ControlTheme>
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Layout;
|
||||
|
||||
namespace Ursa.Themes.Semi.Converters;
|
||||
|
||||
public class FormContentHeightToAlignmentConverter: IValueConverter
|
||||
{
|
||||
public static FormContentHeightToAlignmentConverter Instance = new(32);
|
||||
public double Threshold { get; set; }
|
||||
|
||||
public FormContentHeightToAlignmentConverter()
|
||||
{
|
||||
Threshold = 32;
|
||||
}
|
||||
|
||||
// ReSharper disable once ConvertToPrimaryConstructor
|
||||
// Justification: need to keep the default constructor for XAML
|
||||
public FormContentHeightToAlignmentConverter(double threshold)
|
||||
{
|
||||
Threshold = threshold;
|
||||
}
|
||||
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if(value is not double d) return VerticalAlignment.Center;
|
||||
return d > Threshold ? VerticalAlignment.Top : VerticalAlignment.Center;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Globalization;
|
||||
using Avalonia;
|
||||
using Avalonia.Data.Converters;
|
||||
|
||||
namespace Ursa.Themes.Semi.Converters;
|
||||
|
||||
public class FormContentHeightToMarginConverter: IValueConverter
|
||||
{
|
||||
public static FormContentHeightToMarginConverter Instance = new();
|
||||
public double Threshold { get; set; }
|
||||
|
||||
public FormContentHeightToMarginConverter()
|
||||
{
|
||||
Threshold = 32;
|
||||
}
|
||||
|
||||
// ReSharper disable once ConvertToPrimaryConstructor
|
||||
// Justification: need to keep the default constructor for XAML
|
||||
public FormContentHeightToMarginConverter(double threshold)
|
||||
{
|
||||
Threshold = threshold;
|
||||
}
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if(value is not double d) return new Thickness(0);
|
||||
return d > Threshold ? new Thickness(0, 8, 8, 0) : new Thickness(0, 0, 8, 0);
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user