1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Shared UI - Added tags input control

This commit is contained in:
Robert 2023-08-13 10:41:29 +02:00
parent 3a6171726c
commit e9f2b77fd6
3 changed files with 181 additions and 0 deletions

View File

@ -0,0 +1,78 @@
using System.Text.RegularExpressions;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using ReactiveUI;
namespace Artemis.UI.Shared.TagsInput;
[TemplatePart("PART_TagInputBox", typeof(TextBox))]
public partial class TagsInput : TemplatedControl
{
public TextBox? TagInputBox { get; set; }
public ICommand RemoveTag { get; }
/// <inheritdoc />
public TagsInput()
{
RemoveTag = ReactiveCommand.Create<string>(ExecuteRemoveTag);
}
/// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (TagInputBox != null)
{
TagInputBox.KeyDown -= TagInputBoxOnKeyDown;
TagInputBox.TextChanging -= TagInputBoxOnTextChanging;
}
TagInputBox = e.NameScope.Find<TextBox>("PART_TagInputBox");
if (TagInputBox != null)
{
TagInputBox.KeyDown += TagInputBoxOnKeyDown;
TagInputBox.TextChanging += TagInputBoxOnTextChanging;
}
}
private void ExecuteRemoveTag(string t)
{
Tags.Remove(t);
if (TagInputBox != null)
TagInputBox.IsEnabled = Tags.Count < MaxLength;
}
private void TagInputBoxOnTextChanging(object? sender, TextChangingEventArgs e)
{
if (TagInputBox?.Text == null)
return;
TagInputBox.Text = CleanTagRegex().Replace(TagInputBox.Text.ToLower(), "");
}
private void TagInputBoxOnKeyDown(object? sender, KeyEventArgs e)
{
if (TagInputBox == null)
return;
if (e.Key == Key.Space)
e.Handled = true;
if (e.Key != Key.Enter)
return;
if (string.IsNullOrWhiteSpace(TagInputBox.Text) || Tags.Contains(TagInputBox.Text) || Tags.Count >= MaxLength)
return;
Tags.Add(CleanTagRegex().Replace(TagInputBox.Text.ToLower(), ""));
TagInputBox.Text = "";
TagInputBox.IsEnabled = Tags.Count < MaxLength;
}
[GeneratedRegex("[\\s\\-]+")]
private static partial Regex CleanTagRegex();
}

View File

@ -0,0 +1,54 @@
using System.Collections.ObjectModel;
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
namespace Artemis.UI.Shared.TagsInput;
public partial class TagsInput : TemplatedControl
{
/// <summary>
/// Defines the <see cref="Tags" /> property
/// </summary>
public static readonly StyledProperty<ObservableCollection<string>> TagsProperty =
AvaloniaProperty.Register<TagsInput, ObservableCollection<string>>(nameof(Tags), new ObservableCollection<string>());
/// <summary>
/// Gets or sets the selected tags.
/// </summary>
public ObservableCollection<string> Tags
{
get => GetValue(TagsProperty);
set => SetValue(TagsProperty, value);
}
/// <summary>
/// Defines the <see cref="MaxLength" /> property
/// </summary>
public static readonly StyledProperty<int> MaxLengthProperty =
AvaloniaProperty.Register<TagsInput, int>(nameof(MaxLength), 20);
/// <summary>
/// Gets or sets the max length of each tag
/// </summary>
public int MaxLength
{
get => GetValue(MaxLengthProperty);
set => SetValue(MaxLengthProperty, value);
}
/// <summary>
/// Defines the <see cref="MaxTags" /> property
/// </summary>
public static readonly StyledProperty<int> MaxTagsProperty =
AvaloniaProperty.Register<TagsInput, int>(nameof(MaxTags), 20);
/// <summary>
/// Gets or sets the max amount of tags to be added
/// </summary>
public int MaxTags
{
get => GetValue(MaxTagsProperty);
set => SetValue(MaxTagsProperty, value);
}
}

View File

@ -0,0 +1,49 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:tagsInput="clr-namespace:Artemis.UI.Shared.TagsInput"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
x:CompileBindings="True">
<Design.PreviewWith>
<Border Padding="30" Width="400">
<StackPanel Spacing="20">
<tagsInput:TagsInput Name="TagsInput"/>
<ItemsControl ItemsSource="{CompiledBinding Path=Tags, ElementName=TagsInput}"/>
</StackPanel>
</Border>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type tagsInput:TagsInput}" TargetType="tagsInput:TagsInput">
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<TextBox Watermark="Enter tags" Name="PART_TagInputBox" MaxLines="1" MaxLength="{TemplateBinding MaxLength}">
<TextBox.InnerLeftContent>
<avalonia:MaterialIcon Kind="Tags" Margin="8 0 -2 0"></avalonia:MaterialIcon>
</TextBox.InnerLeftContent>
</TextBox>
<ItemsControl ItemsSource="{CompiledBinding Tags, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tagsInput:TagsInput}}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.DataTemplates>
<DataTemplate DataType="x:String">
<Button Margin="0 5 5 0"
Command="{CompiledBinding RemoveTag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tagsInput:TagsInput}}}"
CommandParameter="{CompiledBinding}">
<StackPanel Orientation="Horizontal" Spacing="5">
<avalonia:MaterialIcon Kind="Close" Margin="-5 0 0 0" Foreground="Gray" />
<TextBlock Text="{CompiledBinding}"></TextBlock>
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.DataTemplates>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>