|
35 | 35 | </div> |
36 | 36 |
|
37 | 37 | <div> |
38 | | - <FluentTextField Name="Tags" Label="Tags" @bind-Value="_note.Tags" /> |
39 | | - <FluentValidationMessage For="@(() => _note.Tags)" /> |
| 38 | + <FluentStack Orientation="Orientation.Vertical"> |
| 39 | + <label>Tags</label> |
| 40 | + <FluentStack Orientation="Orientation.Horizontal" Wrap="true" HorizontalGap="4""> |
| 41 | + @foreach (var tag in _currentTags) |
| 42 | + { |
| 43 | + <FluentBadge Appearance="Appearance.Neutral" OnDismissClick="@((e) => RemoveTag(tag))"> |
| 44 | + @tag |
| 45 | + </FluentBadge> |
| 46 | + } |
| 47 | + </FluentStack> |
| 48 | + <FluentStack Orientation="Orientation.Horizontal"> |
| 49 | + <FluentTextField @bind-Value="_newTagInput" |
| 50 | + Placeholder="Add a tag..." |
| 51 | + @onkeydown="HandleKeyDown" /> |
| 52 | + <FluentButton @onclick="() => AddTag(_newTagInput)" Appearance="Appearance.Lightweight" Size="Size.Small">Add</FluentButton> |
| 53 | + </FluentStack> |
| 54 | + <FluentValidationMessage For="@(() => _note.Tags)" /> |
| 55 | + </FluentStack> |
40 | 56 | </div> |
41 | 57 | </FluentStack> |
42 | 58 | <div style="color: var(--error);"> |
|
78 | 94 | private List<string> _categories = NoteCategories.GetCategories(); |
79 | 95 | private bool _isEditMode = false; |
80 | 96 |
|
| 97 | + private List<string> _currentTags = new(); |
| 98 | + private string _newTagInput = string.Empty; |
| 99 | + |
81 | 100 | protected override void OnInitialized() |
82 | 101 | { |
83 | 102 | // Check if we're editing an existing note or creating a new one |
84 | 103 | _isEditMode = !string.IsNullOrEmpty(Content.RowKey) && !Content.RowKey.Equals(Guid.Empty.ToString(), StringComparison.OrdinalIgnoreCase); |
85 | | - |
| 104 | + |
86 | 105 | if (_isEditMode) |
87 | 106 | { |
88 | 107 | // Editing mode - use the existing note data |
|
93 | 112 | // Create mode - create a new note with the PostId |
94 | 113 | _note = new Note { PostId = Content.PostId }; |
95 | 114 | } |
| 115 | + |
| 116 | + ParseTagsFromString(); |
96 | 117 | } |
97 | 118 |
|
98 | 119 | private async Task SaveAsync() |
|
118 | 139 | await Dialog.CloseAsync(new NoteDialogResult { Action = "Delete", Note = _note }); |
119 | 140 | } |
120 | 141 |
|
| 142 | + private void ParseTagsFromString() |
| 143 | + { |
| 144 | + _currentTags = string.IsNullOrWhiteSpace(_note.Tags) |
| 145 | + ? new List<string>() |
| 146 | + : _note.Tags.Split(',') |
| 147 | + .Select(t => t.Trim().ToLower()) |
| 148 | + .Where(t => !string.IsNullOrWhiteSpace(t)) |
| 149 | + .Distinct() |
| 150 | + .ToList(); |
| 151 | + } |
| 152 | + |
| 153 | + private void SerializeTagsToString() |
| 154 | + { |
| 155 | + _note.Tags = _currentTags.Any() ? string.Join(", ", _currentTags) : null; |
| 156 | + } |
| 157 | + |
| 158 | + private void AddTag(string tag) |
| 159 | + { |
| 160 | + if (string.IsNullOrWhiteSpace(tag)) return; |
| 161 | + |
| 162 | + var normalizedTag = tag.Trim().ToLower(); |
| 163 | + if (!_currentTags.Contains(normalizedTag)) |
| 164 | + { |
| 165 | + _currentTags.Add(normalizedTag); |
| 166 | + SerializeTagsToString(); |
| 167 | + _newTagInput = string.Empty; |
| 168 | + StateHasChanged(); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + private void RemoveTag(string tag) |
| 173 | + { |
| 174 | + _currentTags.Remove(tag); |
| 175 | + SerializeTagsToString(); |
| 176 | + StateHasChanged(); |
| 177 | + } |
| 178 | + |
| 179 | + private void HandleKeyDown(KeyboardEventArgs e) |
| 180 | + { |
| 181 | + if (e.Key == "Enter") |
| 182 | + { |
| 183 | + if (!string.IsNullOrWhiteSpace(_newTagInput)) |
| 184 | + { |
| 185 | + AddTag(_newTagInput); |
| 186 | + } |
| 187 | + } |
| 188 | + } |
121 | 189 |
|
122 | 190 | } |
0 commit comments