11namespace BlazorBootstrap ;
22
3+ using Microsoft . JSInterop ;
4+
35public partial class OTPInput : BlazorBootstrapComponentBase
46{
57 #region Fields and Constants
@@ -22,6 +24,23 @@ protected override void OnParametersSet()
2224 }
2325 }
2426
27+ private async Task SafeInvokeVoidAsync ( string identifier , params object ? [ ] args )
28+ {
29+ try
30+ {
31+ await JSRuntime . InvokeVoidAsync ( identifier , args ) ;
32+ }
33+ catch ( TaskCanceledException )
34+ {
35+ // Component/DOM likely got removed (navigation, conditional render, etc.)
36+ // Treat as benign for focus/value updates.
37+ }
38+ catch ( JSDisconnectedException )
39+ {
40+ // JS runtime no longer available (more common on Server, but safe here too).
41+ }
42+ }
43+
2544 /// <summary>
2645 /// Clears the OTP input fields.
2746 /// </summary>
@@ -34,7 +53,7 @@ public async Task ClearAsync()
3453 await NotifyChangesAsync ( ) ;
3554
3655 if ( Length > 0 )
37- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( 0 ) ) ;
56+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( 0 ) ) ;
3857
3958 await InvokeAsync ( StateHasChanged ) ;
4059 }
@@ -64,28 +83,56 @@ private async Task OnInput(ChangeEventArgs e, int index)
6483 // Clear the input element if it contained invalid characters
6584 if ( ! string . IsNullOrEmpty ( rawValue ) )
6685 {
67- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . SetInputElementValue , GetInputId ( index ) , string . Empty ) ;
86+ await SafeInvokeVoidAsync ( JsInteropUtils . SetInputElementValue , GetInputId ( index ) , string . Empty ) ;
6887 }
6988
7089 await NotifyChangesAsync ( ) ;
7190 return ;
7291 }
7392
74- // If multiple digits were entered (e.g. fast typing or paste), use the last one
75- var digit = numericValue . Length > 1 ? numericValue [ ^ 1 ] . ToString ( ) : numericValue ;
93+ // If multiple digits were entered (e.g. paste), distribute them across the input fields
94+ if ( numericValue . Length > 1 )
95+ {
96+ var digits = numericValue . ToCharArray ( ) ;
97+ var currentInputLength = digits . Length ;
98+
99+ for ( int i = 0 ; i < currentInputLength ; i ++ )
100+ {
101+ var targetIndex = index + i ;
102+ if ( targetIndex < Length )
103+ {
104+ otpValues [ targetIndex ] = digits [ i ] . ToString ( ) ;
105+
106+ // Update the UI value for the current input and subsequent inputs
107+ await SafeInvokeVoidAsync ( JsInteropUtils . SetInputElementValue , GetInputId ( targetIndex ) , otpValues [ targetIndex ] ) ;
108+ }
109+ }
110+
111+ // Move focus to the next input field after the last pasted digit
112+ var nextIndex = index + currentInputLength ;
113+ if ( nextIndex < Length )
114+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( nextIndex ) ) ;
115+ else
116+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( Length - 1 ) ) ;
117+
118+ await NotifyChangesAsync ( ) ;
119+ return ;
120+ }
121+
122+ var digit = numericValue ;
76123
77124 otpValues [ index ] = digit ;
78125
79126 // Reset the input value on the client side if it doesn't match the sanitized digit
80127 if ( rawValue != digit )
81128 {
82- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . SetInputElementValue , GetInputId ( index ) , digit ) ;
129+ await SafeInvokeVoidAsync ( JsInteropUtils . SetInputElementValue , GetInputId ( index ) , digit ) ;
83130 }
84131
85132 // Move focus to the next input field
86133 if ( index < Length - 1 )
87134 {
88- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index + 1 ) ) ;
135+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index + 1 ) ) ;
89136 }
90137
91138 await NotifyChangesAsync ( ) ;
@@ -97,19 +144,19 @@ private async Task OnKeyUp(KeyboardEventArgs e, int index)
97144 if ( e . Key == "Backspace" && index > 0 )
98145 {
99146 otpValues [ index ] = string . Empty ;
100- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index - 1 ) ) ;
147+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index - 1 ) ) ;
101148
102149 // Notify changes
103150 await NotifyChangesAsync ( ) ;
104151 }
105152
106153 // Handle left arrow key to focus on the previous input
107154 if ( e . Key == "ArrowLeft" && index > 0 )
108- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index - 1 ) ) ;
155+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index - 1 ) ) ;
109156
110157 // Handle right arrow key to focus on the next input
111158 if ( e . Key == "ArrowRight" && index < Length - 1 )
112- await JSRuntime . InvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index + 1 ) ) ;
159+ await SafeInvokeVoidAsync ( JsInteropUtils . FocusInputElement , GetInputId ( index + 1 ) ) ;
113160 }
114161
115162 #endregion
0 commit comments