import { EuiFieldText , EuiFlexGroup , EuiFlexItem } from '@elastic/eui' ; import type { FunctionComponent , ReactNode } from 'react' ; import React , { useEffect , useRef } from 'react' ; import useList from 'react-use/lib/useList' ; import useUpdateEffect from 'react-use/lib/useUpdateEffect' ; import { i18n } from '@kbn/i18n' ; import { euiThemeVars } from '@kbn/ui-shared-deps-src/theme' ; export interface SingleCharsFieldProps { defaultValue : string ; length : number ; separator ? : number ; pattern ? : RegExp ; onChange ( value ) : void ; isInvalid ? : boolean ; autoFocus ? : boolean ; } export const SingleCharsField = ( { defaultValue , length , separator , pattern = /^[0-9]$/ , onChange , isInvalid , autoFocus = false , } ) => { const sanitize = ( str ) => { return str . split ( '' ) . filter ( ( char ) => char . match ( pattern ) ) . join ( '' ) . substr ( 0 , length ) ; } ; const inputRefs = useRef < Array < HTMLInputElement | null > > ( [ ] ) ; const [ chars , { set , updateAt } ] = useList ( sanitize ( defaultValue ) . split ( '' ) ) ; const focusField = ( i ) => { const input = inputRefs . current [ i ] ; if ( input ) { input . focus ( ) ; } } ; useUpdateEffect ( ( ) => { onChange ( chars . join ( '' ) ) ; } , [ chars ] ) ; useEffect ( ( ) => { if ( autoFocus ) { focusField ( 0 ) ; } } , [ autoFocus ] ) ; const children = [ ] ; for ( let i = 0 ; i < length ; i ++ ) { if ( separator && i !== 0 && i % separator === 0 ) { children . push ( < EuiFlexItem key = { `${ i }separator` } grow = { false } style = { { width : parseInt ( euiThemeVars . euiFormControlHeight , 10 ) / 8 } } />); } children . push ( < EuiFlexItem key = { i } grow = { false } style = { { width : euiThemeVars . euiFormControlHeight } } > < EuiFieldText inputRef = { ( el ) => { inputRefs . current [ i ] = el ; } } value = { chars [ i ] ?? '' } onChange = { ( event ) => { event . stopPropagation ( ) ; } } onInput = { ( event ) => { if ( event . currentTarget . value !== '' && sanitize ( event . currentTarget . value ) === '' ) { return event . preventDefault ( ) ; } updateAt ( i , event . currentTarget . value ) ; if ( event . currentTarget . value ) { focusField ( i + 1 ) ; } } } onKeyDown = { ( event ) => { if ( event . key === 'Backspace' ) { if ( event . currentTarget . value === '' ) { updateAt ( i - 1 , event . currentTarget . value ) ; focusField ( i - 1 ) ; } } else if ( event . key === 'ArrowLeft' ) { focusField ( i - 1 ) ; } else if ( event . key === 'ArrowRight' ) { focusField ( i + 1 ) ; } } } onPaste = { ( event ) => { const text = sanitize ( event . clipboardData . getData ( 'text' ) ) ; set ( text . split ( '' ) ) ; focusField ( Math . min ( text . length , length - 1 ) ) ; event . preventDefault ( ) ; } } onFocus = { ( event ) => { const input = event . currentTarget ; setTimeout ( ( ) => input . select ( ) , 0 ) ; } } maxLength = { 1 } isInvalid = { isInvalid } style = { { textAlign : 'center' } } aria - label = { i18n . translate ( 'interactiveSetup.singleCharsField.digitLabel' , { defaultMessage : 'Digit {index}' , values : { index : i + 1 } , } ) } /> < /EuiFlexItem>); } return ( < EuiFlexGroup responsive = { false } alignItems = "center" justifyContent = "center" gutterSize = "s" > { children } < /EuiFlexGroup>); } ;