mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Shared UI - Added tags input control
This commit is contained in:
parent
3a6171726c
commit
e9f2b77fd6
78
src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs
Normal file
78
src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs
Normal 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();
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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>
|
||||||
Loading…
x
Reference in New Issue
Block a user