From e9f2b77fd63808d8efc00f3d55a98d53930d62e3 Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 13 Aug 2023 10:41:29 +0200 Subject: [PATCH] Shared UI - Added tags input control --- .../Controls/TagsInput/TagsInput.cs | 78 +++++++++++++++++++ .../TagsInput/TagsInput.properties.cs | 54 +++++++++++++ .../Controls/TagsInput/TagsInputStyles.axaml | 49 ++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs create mode 100644 src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.properties.cs create mode 100644 src/Artemis.UI.Shared/Controls/TagsInput/TagsInputStyles.axaml diff --git a/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs new file mode 100644 index 000000000..3b94d3766 --- /dev/null +++ b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.cs @@ -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; } + + /// + public TagsInput() + { + RemoveTag = ReactiveCommand.Create(ExecuteRemoveTag); + } + + /// + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + if (TagInputBox != null) + { + TagInputBox.KeyDown -= TagInputBoxOnKeyDown; + TagInputBox.TextChanging -= TagInputBoxOnTextChanging; + } + + TagInputBox = e.NameScope.Find("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(); +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.properties.cs b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.properties.cs new file mode 100644 index 000000000..2660e7bb8 --- /dev/null +++ b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInput.properties.cs @@ -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 +{ + /// + /// Defines the property + /// + public static readonly StyledProperty> TagsProperty = + AvaloniaProperty.Register>(nameof(Tags), new ObservableCollection()); + + /// + /// Gets or sets the selected tags. + /// + public ObservableCollection Tags + { + get => GetValue(TagsProperty); + set => SetValue(TagsProperty, value); + } + + /// + /// Defines the property + /// + public static readonly StyledProperty MaxLengthProperty = + AvaloniaProperty.Register(nameof(MaxLength), 20); + + /// + /// Gets or sets the max length of each tag + /// + public int MaxLength + { + get => GetValue(MaxLengthProperty); + set => SetValue(MaxLengthProperty, value); + } + + /// + /// Defines the property + /// + public static readonly StyledProperty MaxTagsProperty = + AvaloniaProperty.Register(nameof(MaxTags), 20); + + /// + /// Gets or sets the max amount of tags to be added + /// + public int MaxTags + { + get => GetValue(MaxTagsProperty); + set => SetValue(MaxTagsProperty, value); + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Controls/TagsInput/TagsInputStyles.axaml b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInputStyles.axaml new file mode 100644 index 000000000..e2a0ff8e5 --- /dev/null +++ b/src/Artemis.UI.Shared/Controls/TagsInput/TagsInputStyles.axaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file