Skip to content

Commit 68b6a50

Browse files
authored
MudCodeInput (#97)
* Initialize * Cleanup * Final Cleanup
1 parent 7ba773a commit 68b6a50

File tree

6 files changed

+332
-1
lines changed

6 files changed

+332
-1
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@namespace MudExtensions
2+
@typeparam T
3+
@inherits MudFormComponent<T, string>
4+
5+
<div class="@Classname" style="@Style">
6+
@for (int i = 0; i < Count; i++)
7+
{
8+
int a = i;
9+
<MudTextField @ref="_elementReferences[a]" T="T" Class="@InputClassname" Style="@(Margin == Margin.Dense ? "width: 32px" : "width: 42px")" MaxLength="1"
10+
@onkeydown="HandleKeyDown" @onfocus="@(() => CheckFocus(a))" @onblur="SetValue"
11+
Variant="@Variant" Margin="@Margin" Disabled="Disabled" ReadOnly="ReadOnly" Immediate="true" InputType="InputType" />
12+
}
13+
</div>
14+
15+
<style>
16+
.justify-text-center input {
17+
text-align: center;
18+
}
19+
20+
.mud-code input {
21+
padding: 12px 0px !important;
22+
}
23+
</style>
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
using System;
2+
using System.Text.RegularExpressions;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Components;
5+
using Microsoft.AspNetCore.Components.Web;
6+
using MudBlazor;
7+
using MudBlazor.Utilities;
8+
using MudExtensions.Extensions;
9+
10+
namespace MudExtensions
11+
{
12+
public partial class MudCodeInput<T> : MudFormComponent<T, string>
13+
{
14+
public MudCodeInput() : base(new DefaultConverter<T>()) { }
15+
16+
protected string Classname =>
17+
new CssBuilder($"d-flex gap-{Spacing}")
18+
.AddClass(Class)
19+
.Build();
20+
21+
protected string InputClassname =>
22+
new CssBuilder("justify-text-center")
23+
.AddClass("mud-code", Variant != Variant.Text)
24+
.AddClass(ClassInput)
25+
.Build();
26+
27+
private List<MudTextField<T>> _elementReferences = new();
28+
29+
/// <summary>
30+
/// The CSS classes for each input, seperated by space.
31+
/// </summary>
32+
[Parameter]
33+
[Category(CategoryTypes.FormComponent.Behavior)]
34+
public string ClassInput { get; set; }
35+
36+
/// <summary>
37+
/// Type of the input element. It should be a valid HTML5 input type.
38+
/// </summary>
39+
[Parameter]
40+
[Category(CategoryTypes.FormComponent.Behavior)]
41+
public InputType InputType { get; set; } = InputType.Text;
42+
43+
/// <summary>
44+
/// The value of the input.
45+
/// </summary>
46+
private T _theValue;
47+
[Parameter]
48+
[Category(CategoryTypes.FormComponent.Behavior)]
49+
public T Value
50+
{
51+
get => _theValue;
52+
set
53+
{
54+
if (Converter.Set(_theValue) == Converter.Set(value))
55+
{
56+
return;
57+
}
58+
_theValue = value;
59+
SetValueFromOutside(_theValue).AndForgetExt();
60+
}
61+
}
62+
63+
/// <summary>
64+
/// The event fires when value changed.
65+
/// </summary>
66+
[Parameter]
67+
[Category(CategoryTypes.FormComponent.Behavior)]
68+
public EventCallback<T> ValueChanged { get; set; }
69+
70+
private int _count;
71+
/// <summary>
72+
/// The number of text fields.
73+
/// </summary>
74+
[Parameter]
75+
[Category(CategoryTypes.FormComponent.Behavior)]
76+
public int Count
77+
{
78+
get => _count;
79+
set
80+
{
81+
if (value == _count || value < 0)
82+
{
83+
return;
84+
}
85+
86+
if (12 < value)
87+
{
88+
_count = 12;
89+
}
90+
else
91+
{
92+
_count = value;
93+
}
94+
}
95+
}
96+
97+
/// <summary>
98+
/// Determines the spacing between each input.
99+
/// </summary>
100+
[Parameter]
101+
[Category(CategoryTypes.FormComponent.Behavior)]
102+
public int Spacing { get; set; } = 2;
103+
104+
[Parameter]
105+
[Category(CategoryTypes.FormComponent.Behavior)]
106+
public bool Disabled { get; set; }
107+
108+
[Parameter]
109+
[Category(CategoryTypes.FormComponent.Behavior)]
110+
public bool ReadOnly { get; set; }
111+
112+
[Parameter]
113+
[Category(CategoryTypes.FormComponent.Behavior)]
114+
public Variant Variant { get; set; } = Variant.Text;
115+
116+
[Parameter]
117+
[Category(CategoryTypes.FormComponent.Behavior)]
118+
public Margin Margin { get; set; }
119+
120+
protected async Task HandleKeyDown(KeyboardEventArgs arg)
121+
{
122+
if (Disabled || ReadOnly)
123+
{
124+
return;
125+
}
126+
127+
if (RuntimeLocation.IsClientSide)
128+
{
129+
await Task.Delay(10);
130+
}
131+
132+
if (arg.Key == "Backspace" || arg.Key == "ArrowLeft")
133+
{
134+
await FocusPrevious();
135+
return;
136+
}
137+
138+
if (arg.Key.Length == 1 || arg.Key == "ArrowRight")
139+
{
140+
await FocusNext();
141+
}
142+
143+
}
144+
145+
private int _lastFocusedIndex = 0;
146+
protected void CheckFocus(int count)
147+
{
148+
_lastFocusedIndex = count;
149+
}
150+
151+
public async Task FocusNext()
152+
{
153+
if (_lastFocusedIndex >= Count - 1)
154+
{
155+
await _elementReferences[_lastFocusedIndex].BlurAsync();
156+
await _elementReferences[_lastFocusedIndex].FocusAsync();
157+
return;
158+
}
159+
await _elementReferences[_lastFocusedIndex + 1].FocusAsync();
160+
}
161+
162+
public async Task FocusPrevious()
163+
{
164+
if (_lastFocusedIndex == 0)
165+
{
166+
await _elementReferences[_lastFocusedIndex].BlurAsync();
167+
await _elementReferences[_lastFocusedIndex].FocusAsync();
168+
return;
169+
}
170+
await _elementReferences[_lastFocusedIndex - 1].FocusAsync();
171+
}
172+
173+
protected override void OnInitialized()
174+
{
175+
base.OnInitialized();
176+
SyncReferences();
177+
}
178+
179+
private void SyncReferences()
180+
{
181+
_elementReferences.Clear();
182+
for (int i = 0; i < 12; i++)
183+
{
184+
_elementReferences.Add(new MudTextField<T>());
185+
}
186+
}
187+
188+
public async Task SetValue()
189+
{
190+
string result = "";
191+
for (int i = 0; i < Count; i++)
192+
{
193+
var val = _elementReferences[i].Value?.ToString();
194+
if (val == null)
195+
{
196+
continue;
197+
}
198+
199+
result += val;
200+
}
201+
202+
Value = Converter.Get(result);
203+
await ValueChanged.InvokeAsync(Value);
204+
}
205+
206+
public async Task SetValueFromOutside(T value)
207+
{
208+
string val = Converter.Set(value);
209+
if (Count < val.Length)
210+
{
211+
val = val.Substring(0, Count);
212+
}
213+
Value = Converter.Get(val);
214+
for (int i = 0; i < Count; i++)
215+
{
216+
if (i < val.Length)
217+
{
218+
await _elementReferences[i].SetText(val[i].ToString());
219+
}
220+
else
221+
{
222+
await _elementReferences[i].SetText(null);
223+
}
224+
}
225+
226+
await ValueChanged.InvokeAsync(Value);
227+
StateHasChanged();
228+
}
229+
230+
}
231+
}

ComponentViewer.Docs/Pages/Components/ApiPage.razor

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@
2424
</MudTable>
2525
</ExampleCard>
2626

27+
<ExampleCard Title="Api - MudCodeInput" HasExpansionPanel="true">
28+
<MudTable Items="@(typeof(MudCodeInput<string>).GetProperties().Where(x => x.Name != "FieldId" && x.Name != "UserAttributes").OrderBy(x => x.Name).ToList())">
29+
<HeaderContent>
30+
<MudTh>Name</MudTh>
31+
<MudTh>Type</MudTh>
32+
<MudTh>Default</MudTh>
33+
</HeaderContent>
34+
<RowTemplate>
35+
<MudTd>@context.Name</MudTd>
36+
<MudTd>@context.PropertyType.Name</MudTd>
37+
<MudTd>
38+
@if (true)
39+
{
40+
MudCodeInput<string> instance = new();
41+
<MudText Typo="Typo.body2">@(context.GetValue(instance)?.ToString() ?? "null")</MudText>
42+
}
43+
</MudTd>
44+
</RowTemplate>
45+
</MudTable>
46+
</ExampleCard>
47+
2748
<ExampleCard Title="Api - MudColorProvider" HasExpansionPanel="true">
2849
<MudTable Items="@(typeof(MudColorProvider).GetProperties().Where(x => x.Name != "FieldId" && x.Name != "UserAttributes").OrderBy(x => x.Name).ToList())">
2950
<HeaderContent>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@page "/mudcodeinput"
2+
@using ComponentViewer.Docs.Pages.Examples
3+
4+
<ExamplePage Title="MudCodeInput">
5+
<ExampleCard ExampleName="CodeInputExample1" Title="Usage" Description="">
6+
<CodeInputExample1 />
7+
</ExampleCard>
8+
</ExamplePage>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@using Microsoft.AspNetCore.Components
2+
@using MudBlazor.Extensions
3+
4+
<MudGrid>
5+
<MudItem xs="12" sm="8" Class="d-flex flex-column gap-8 align-center justify-center" Style="height: 500px">
6+
<MudCodeInput @ref="_textFieldGroup" T="string" @bind-Value="@_value" Count="@_count" Spacing="_spacing" Variant="@_variant" Margin="_margin"
7+
Disabled="@_disabled" ReadOnly="@_readonly" />
8+
</MudItem>
9+
10+
<MudItem xs="12" sm="4">
11+
<MudStack Spacing="4">
12+
<MudText>Value: @_value</MudText>
13+
<MudTextField @bind-Value="@_value" Immediate="true" Label="Two Way Bind Controller" />
14+
<MudNumericField @bind-Value="_count" Label="Count" Variant="Variant.Outlined" />
15+
<MudNumericField @bind-Value="_spacing" Label="Spacing" Variant="Variant.Outlined" />
16+
<MudSelect @bind-Value="_variant" Variant="Variant.Outlined" Label="Variant">
17+
@foreach (var item in Enum.GetValues<Variant>())
18+
{
19+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
20+
}
21+
</MudSelect>
22+
<MudSelect @bind-Value="_margin" Variant="Variant.Outlined" Label="Margin">
23+
@foreach (var item in Enum.GetValues<Margin>())
24+
{
25+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
26+
}
27+
</MudSelect>
28+
<MudSwitchM3 @bind-Checked="@_disabled" Label="Disabled" Color="Color.Primary" />
29+
<MudSwitchM3 @bind-Checked="@_readonly" Label="ReadOnly" Color="Color.Primary" />
30+
</MudStack>
31+
</MudItem>
32+
</MudGrid>
33+
34+
@code{
35+
MudCodeInput<string> _textFieldGroup;
36+
string _value;
37+
int _count = 4;
38+
int _spacing = 2;
39+
Variant _variant = Variant.Outlined;
40+
Margin _margin;
41+
bool _disabled;
42+
bool _readonly;
43+
}

ComponentViewer.Docs/Pages/Index.razor

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
<ComponentCard Title="MudAnimate"
3838
Description="A revolutionary next generation animate component." />
3939
</MudItem>
40+
41+
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
42+
<ComponentCard Title="MudCodeInput"
43+
Description="A group of textfields that each one can contain a char." />
44+
</MudItem>
4045

4146
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
4247
<ComponentCard Title="MudColorProvider"
@@ -110,7 +115,7 @@
110115

111116
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
112117
<ComponentCard Title="MudSwitchM3"
113-
Description="Material 3 switch component that has all MudBlazor feat" />
118+
Description="Material 3 switch component that has all MudBlazor features." />
114119
</MudItem>
115120

116121
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">

0 commit comments

Comments
 (0)