import invariant from 'invariant' ; import React , { cloneElement , KeyboardEvent , useEffect , useRef } from 'react' ; import { useTypeaheadContext } from '../core/Context' ; import { isSelectable } from '../utils' ; import { ShouldSelect } from '../types' ; function interpolateStyle ( styles , attr , subattr = '' ) { if ( subattr ) { subattr = subattr . replace ( subattr [ 0 ] , subattr [ 0 ] . toUpperCase ( ) ) ; } return [ 'Top' , 'Right' , 'Bottom' , 'Left' ] . map ( ( dir ) => styles [ `${ attr }${ dir }${ subattr }` as keyof CSSStyleDeclaration ] ) . join ( ' ' ) ; } function copyStyles ( inputNode , hintNode ) { if ( ! inputNode || ! hintNode ) { return ; } const inputStyle = window . getComputedStyle ( inputNode ) ; hintNode . style . borderStyle = interpolateStyle ( inputStyle , 'border' , 'style' ) ; hintNode . style . borderWidth = interpolateStyle ( inputStyle , 'border' , 'width' ) ; hintNode . style . fontSize = inputStyle . fontSize ; hintNode . style . height = inputStyle . height ; hintNode . style . lineHeight = inputStyle . lineHeight ; hintNode . style . margin = interpolateStyle ( inputStyle , 'margin' ) ; hintNode . style . padding = interpolateStyle ( inputStyle , 'padding' ) ; } export function defaultShouldSelect ( e , shouldSelect ? ) { let shouldSelectHint = false ; const { currentTarget , key } = e ; if ( key === 'ArrowRight' ) { shouldSelectHint = isSelectable ( currentTarget ) ? currentTarget . selectionStart === currentTarget . value . length : true ; } if ( key === 'Tab' ) { e . preventDefault ( ) ; shouldSelectHint = true ; } return typeof shouldSelect === 'function' ? shouldSelect ( shouldSelectHint , e ) : shouldSelectHint ; } interface Config { children : JSX . Element ; shouldSelect ? : ShouldSelect ; } export const useHint = ( { children , shouldSelect } ) => { invariant ( React . Children . count ( children ) === 1 , '`useHint` expects one child.' ) ; const { hintText , initialItem , inputNode , onAdd } = useTypeaheadContext ( ) ; const hintRef = useRef < HTMLInputElement | null > ( null ) ; const onKeyDown = ( e ) => { if ( hintText && initialItem && defaultShouldSelect ( e , shouldSelect ) ) { onAdd ( initialItem ) ; } children . props . onKeyDown && children . props . onKeyDown ( e ) ; } ; useEffect ( ( ) => { copyStyles ( inputNode , hintRef . current ) ; } ) ; return { child : cloneElement ( children , { ... children . props , onKeyDown } ) , hintRef , hintText , } ; } ; interface HintProps extends Config { className ? : string ; } const Hint = ( { className , ... props } ) : JSX . Element => { const { child , hintRef , hintText } = useHint ( props ) ; return ( < div className = { className } style = { { display : 'flex' , flex : 1 , height : '100%' , position : 'relative' , } } > { child } < input aria - hidden className = "rbt-input-hint" ref = { hintRef } readOnly style = { { backgroundColor : 'transparent' , borderColor : 'transparent' , boxShadow : 'none' , color : 'rgba(0, 0, 0, 0.54)' , left : 0 , pointerEvents : 'none' , position : 'absolute' , top : 0 , width : '100%' , } } tabIndex = { - 1 } value = { hintText } / > < /div>); } ; export default Hint ;