@@ -32,6 +32,13 @@ export class InputOTP implements ComponentInterface {
3232 */
3333 private focusedValue ?: string | number | null ;
3434
35+ /**
36+ * Tracks whether the user is navigating through input boxes using keyboard navigation
37+ * (arrow keys, tab) versus mouse clicks. This is used to determine the appropriate
38+ * focus behavior when an input box is focused.
39+ */
40+ private isKeyboardNavigation = false ;
41+
3542 @Element ( ) el ! : HTMLIonInputOtpElement ;
3643
3744 @State ( ) private inputValues : string [ ] = [ ] ;
@@ -261,9 +268,9 @@ export class InputOTP implements ComponentInterface {
261268
262269 /**
263270 * Sets focus to an input box.
264- * @param index The index of the input box to focus. If not provided,
265- * focuses the first empty input box or the last input if all are filled .
266- * The input boxes start at index 0 .
271+ * @param index - The index of the input box to focus (0-based).
272+ * If provided and the input box has a value, the input box at that index will be focused .
273+ * Otherwise, the first empty input box or the last input if all are filled will be focused .
267274 */
268275 @Method ( )
269276 async setFocus ( index ?: number ) {
@@ -406,6 +413,13 @@ export class InputOTP implements ComponentInterface {
406413 const { length } = this ;
407414 const rtl = isRTL ( this . el ) ;
408415
416+ // Do not process the paste shortcut to avoid changing
417+ // the value to the letter "v" on paste
418+ const isPasteShortcut = ( event . metaKey || event . ctrlKey ) && event . key . toLowerCase ( ) === 'v' ;
419+ if ( isPasteShortcut ) {
420+ return ;
421+ }
422+
409423 if ( event . key === 'Backspace' ) {
410424 if ( this . inputValues [ index ] ) {
411425 // Remove the value at the current index
@@ -431,6 +445,7 @@ export class InputOTP implements ComponentInterface {
431445 this . focusPrevious ( index ) ;
432446 }
433447 } else if ( event . key === 'ArrowLeft' || event . key === 'ArrowRight' ) {
448+ this . isKeyboardNavigation = true ;
434449 event . preventDefault ( ) ;
435450 const isLeft = event . key === 'ArrowLeft' ;
436451 const shouldMoveNext = ( isLeft && rtl ) || ( ! isLeft && ! rtl ) ;
@@ -444,6 +459,7 @@ export class InputOTP implements ComponentInterface {
444459 this . focusPrevious ( index ) ;
445460 }
446461 } else if ( event . key === 'Tab' ) {
462+ this . isKeyboardNavigation = true ;
447463 // Let all tab events proceed normally
448464 return ;
449465 }
@@ -551,6 +567,13 @@ export class InputOTP implements ComponentInterface {
551567
552568 /**
553569 * Handles the focus behavior for the input OTP component.
570+ *
571+ * Focus behavior:
572+ * 1. Keyboard navigation: Allow normal focus movement
573+ * 2. Mouse click:
574+ * - If clicked box has value: Focus that box
575+ * - If clicked box is empty: Focus first empty box
576+ *
554577 * Emits the `ionFocus` event when the input group gains focus.
555578 */
556579 private onFocus = ( index : number ) => ( event : FocusEvent ) => {
@@ -563,10 +586,25 @@ export class InputOTP implements ComponentInterface {
563586 }
564587 this . hasFocus = true ;
565588
566- // When an input receives focus, make it the only tabbable element
589+ let finalIndex = index ;
590+
591+ if ( ! this . isKeyboardNavigation ) {
592+ // If the clicked box has a value, focus it
593+ // Otherwise focus the first empty box
594+ const targetIndex = this . inputValues [ index ] ? index : this . getFirstEmptyIndex ( ) ;
595+ finalIndex = targetIndex === - 1 ? this . length - 1 : targetIndex ;
596+
597+ // Focus the target box
598+ this . inputRefs [ finalIndex ] ?. focus ( ) ;
599+ }
600+
601+ // Update tabIndexes to match the focused box
567602 inputRefs . forEach ( ( input , i ) => {
568- input . tabIndex = i === index ? 0 : - 1 ;
603+ input . tabIndex = i === finalIndex ? 0 : - 1 ;
569604 } ) ;
605+
606+ // Reset the keyboard navigation flag
607+ this . isKeyboardNavigation = false ;
570608 } ;
571609
572610 /**
0 commit comments