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"
|
x:DataType="vm:FormDemoViewModel"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel>
|
<StackPanel HorizontalAlignment="Left" Margin="24 0 0 0 ">
|
||||||
<Grid RowDefinitions="Auto, Auto" ColumnDefinitions="Auto, Auto">
|
<u:Divider Content="Simple Form"/>
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Label Position" VerticalAlignment="Center"></TextBlock>
|
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Label Alignment" VerticalAlignment="Center"></TextBlock>
|
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||||
<u:EnumSelector Grid.Row="0" Grid.Column="1"
|
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||||
Name="position"
|
<TextBox Width="300" u:FormItem.Label="Message" Classes="TextArea"/>
|
||||||
EnumType="common:Position"
|
</u:Form>
|
||||||
Value="{x:Static common:Position.Top}" />
|
<u:Divider Content="Form accessibility"/>
|
||||||
<u:EnumSelector Grid.Row="1" Grid.Column="1"
|
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||||
Name="alignment"
|
<TextBox Width="300" u:FormItem.Label="_Name" />
|
||||||
EnumType="HorizontalAlignment"
|
<TextBox Width="300" u:FormItem.Label="_Email" />
|
||||||
Value="{x:Static HorizontalAlignment.Left}" />
|
<TextBox Width="300" u:FormItem.Label="_Message" Classes="TextArea"/>
|
||||||
</Grid>
|
</u:Form>
|
||||||
<u:Form
|
<u:Divider Content="Label on top"/>
|
||||||
DataContext="{Binding Model}"
|
<u:Form LabelPosition="Top" LabelWidth="*">
|
||||||
LabelAlignment="{Binding #alignment.Value}"
|
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||||
LabelPosition="{Binding #position.Value}"
|
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||||
LabelWidth="*">
|
</u:Form>
|
||||||
<u:FormGroup Header="Information">
|
<u:Divider Content="FormGroup"/>
|
||||||
<TextBox
|
<u:Form LabelPosition="Left" LabelWidth="*">
|
||||||
Width="300"
|
<u:FormGroup Header="Basic Information">
|
||||||
u:FormItem.IsRequired="True"
|
<TextBox Width="300" u:FormItem.Label="Name" />
|
||||||
u:FormItem.Label="Name"
|
<TextBox Width="300" u:FormItem.Label="Email" />
|
||||||
Text="{Binding Name}" />
|
|
||||||
<TextBox
|
|
||||||
Width="300"
|
|
||||||
u:FormItem.Label="Email"
|
|
||||||
Text="{Binding Email}" />
|
|
||||||
<u:NumericDoubleUpDown Value="{Binding Number}" u:FormItem.Label="Number" Width="300"/>
|
|
||||||
</u:FormGroup>
|
</u:FormGroup>
|
||||||
<u:FormItem Label="Please select a Date">
|
<u:FormGroup Header="Education Information">
|
||||||
<CalendarDatePicker SelectedDate="{Binding Date}" />
|
<TextBox Width="300" u:FormItem.Label="Collage" />
|
||||||
</u:FormItem>
|
<u:FormItem Label="Study Time">
|
||||||
|
<u:DateRangePicker Width="300"></u:DateRangePicker>
|
||||||
<u:FormGroup Header="Education">
|
</u:FormItem>
|
||||||
<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>
|
</u:FormGroup>
|
||||||
<u:FormItem Label="Click to Submit">
|
<Button Content="Submit" Theme="{DynamicResource SolidButton}" u:FormItem.NoLabel="True" HorizontalAlignment="Stretch"/>
|
||||||
<Button Content="Button With Label" />
|
</u:Form>
|
||||||
</u:FormItem>
|
<u:Divider Content="MVVM setup and validation" />
|
||||||
<CheckBox Content="I Agree User Agreement" u:FormItem.NoLabel="True"></CheckBox>
|
<u:Form LabelPosition="Left" LabelWidth="*" DataContext="{Binding Model}">
|
||||||
<u:FormItem NoLabel="True">
|
<TextBox Width="300" u:FormItem.Label="Name" Text="{Binding Name}"/>
|
||||||
<Button HorizontalAlignment="Left" Content="No Label" />
|
<TextBox Width="300" u:FormItem.Label="Email" Text="{Binding Email}" />
|
||||||
</u:FormItem>
|
|
||||||
</u:Form>
|
</u:Form>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -1,24 +1,30 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
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 -->
|
<!-- Add Resources Here -->
|
||||||
<ControlTheme x:Key="{x:Type u:Form}" TargetType="u:Form">
|
<ControlTheme x:Key="{x:Type u:Form}"
|
||||||
<Setter Property="Grid.IsSharedSizeScope" Value="False" />
|
TargetType="u:Form">
|
||||||
|
<Setter Property="Grid.IsSharedSizeScope"
|
||||||
|
Value="False" />
|
||||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:Form">
|
<ControlTemplate TargetType="u:Form">
|
||||||
<DataValidationErrors>
|
<DataValidationErrors>
|
||||||
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
<ItemsPresenter
|
||||||
|
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
</DataValidationErrors>
|
</DataValidationErrors>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
<Style Selector="^:fixed-width">
|
<Style Selector="^:fixed-width">
|
||||||
<Setter Property="Grid.IsSharedSizeScope" Value="True" />
|
<Setter Property="Grid.IsSharedSizeScope"
|
||||||
|
Value="True" />
|
||||||
</Style>
|
</Style>
|
||||||
</ControlTheme>
|
</ControlTheme>
|
||||||
|
|
||||||
<ControlTheme x:Key="{x:Type u:FormGroup}" TargetType="u:FormGroup">
|
<ControlTheme x:Key="{x:Type u:FormGroup}"
|
||||||
|
TargetType="u:FormGroup">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:FormGroup">
|
<ControlTemplate TargetType="u:FormGroup">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@@ -32,15 +38,17 @@
|
|||||||
Height="1"
|
Height="1"
|
||||||
Margin="0,8"
|
Margin="0,8"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
IsVisible="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={x:Static ObjectConverters.IsNotNull}}"
|
Fill="{DynamicResource SemiColorBorder}"
|
||||||
Fill="{DynamicResource SemiColorBorder}" />
|
IsVisible="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||||
<ItemsPresenter ItemsPanel="{TemplateBinding ItemsPanel}" />
|
<ItemsPresenter
|
||||||
|
ItemsPanel="{TemplateBinding ItemsPanel}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
</ControlTheme>
|
</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="Margin" Value="0 8" />
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:FormItem">
|
<ControlTemplate TargetType="u:FormItem">
|
||||||
@@ -50,13 +58,19 @@
|
|||||||
Margin="0,0,0,4"
|
Margin="0,0,0,4"
|
||||||
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<ContentPresenter Content="{TemplateBinding Label}" FontWeight="{DynamicResource TextBlockTitleFontWeight}" />
|
<Label
|
||||||
|
Content="{TemplateBinding Label}"
|
||||||
|
Background="Transparent"
|
||||||
|
FontWeight="{DynamicResource TextBlockTitleFontWeight}"
|
||||||
|
Target="{Binding #PART_ContentPresenter.Content}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Foreground="{DynamicResource SemiRed6}"
|
Foreground="{DynamicResource SemiRed6}"
|
||||||
IsVisible="{TemplateBinding IsRequired}"
|
IsVisible="{TemplateBinding IsRequired}"
|
||||||
Text="*" />
|
Text="*" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ContentPresenter Content="{TemplateBinding Content}" />
|
<ContentPresenter
|
||||||
|
Name="PART_ContentPresenter"
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
@@ -65,7 +79,8 @@
|
|||||||
<ControlTemplate TargetType="u:FormItem">
|
<ControlTemplate TargetType="u:FormItem">
|
||||||
<Grid RowDefinitions="*, *">
|
<Grid RowDefinitions="*, *">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
<ColumnDefinition Width="Auto"
|
||||||
|
SharedSizeGroup="Label" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Border
|
<Border
|
||||||
@@ -74,10 +89,16 @@
|
|||||||
Width="{TemplateBinding LabelWidth}">
|
Width="{TemplateBinding LabelWidth}">
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Name="PART_LabelPanel"
|
Name="PART_LabelPanel"
|
||||||
Margin="8,8,8,0"
|
Margin="{Binding #PART_Label.Bounds.Height, Converter={x:Static converters:FormContentHeightToMarginConverter.Instance}}"
|
||||||
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
HorizontalAlignment="{TemplateBinding LabelAlignment}"
|
||||||
|
VerticalAlignment="{Binding #PART_ContentPresenter.Bounds.Height, Converter={x:Static converters:FormContentHeightToAlignmentConverter.Instance}}"
|
||||||
Orientation="Horizontal">
|
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
|
<TextBlock
|
||||||
Foreground="{DynamicResource SemiRed6}"
|
Foreground="{DynamicResource SemiRed6}"
|
||||||
IsVisible="{TemplateBinding IsRequired}"
|
IsVisible="{TemplateBinding IsRequired}"
|
||||||
@@ -85,8 +106,11 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
|
Name="PART_ContentPresenter"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
Content="{TemplateBinding Content}" />
|
Content="{TemplateBinding Content}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
@@ -95,9 +119,10 @@
|
|||||||
<Style Selector="^:no-label">
|
<Style Selector="^:no-label">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<ControlTemplate TargetType="u:FormItem">
|
<ControlTemplate TargetType="u:FormItem">
|
||||||
<ContentPresenter Content="{TemplateBinding Content}" />
|
<ContentPresenter
|
||||||
|
Content="{TemplateBinding Content}" />
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
</ControlTheme>
|
</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