1- import { Key , KeybindingContext , useNav } from 'react-keyhooks' ;
1+ import { Key , KeybindingContext , KeybindingProvider , useNav } from 'react-keyhooks' ;
22import * as React from 'react' ;
33import { Input , InputProps , SetInputFxn , useDebounce , useInput } from '../input/input' ;
44import ThemeDiv from '../theme-div/theme-div' ;
@@ -27,18 +27,36 @@ export const Autocomplete = (
2727 icon ?: string ;
2828 inputref ?: React . MutableRefObject < HTMLInputElement > ;
2929 }
30+ ) => {
31+ return (
32+ < KeybindingProvider >
33+ < RenderAutocomplete { ...props } />
34+ </ KeybindingProvider >
35+ ) ;
36+ } ;
37+
38+ export const RenderAutocomplete = (
39+ props : React . InputHTMLAttributes < HTMLInputElement > & {
40+ items : string [ ] ;
41+ inputStyle ?: React . CSSProperties ;
42+ onItemClick ?: ( item : string ) => void ;
43+ icon ?: string ;
44+ inputref ?: React . MutableRefObject < HTMLInputElement > ;
45+ }
3046) => {
3147 const [ curItems , setCurItems ] = React . useState ( props . items || [ ] ) ;
3248 const nullInputRef = React . useRef < HTMLInputElement > ( null ) ;
3349 const inputRef = props . inputref || nullInputRef ;
3450 const autocompleteRef = React . useRef ( null ) ;
3551 const [ showSuggestions , setShowSuggestions ] = React . useState ( false ) ;
3652 const [ pos , nav , reset ] = useNav ( props . items . length ) ;
53+ const menuRef = React . useRef ( null ) ;
3754
3855 React . useEffect ( ( ) => {
3956 function unfocus ( e : any ) {
4057 if ( autocompleteRef . current && ! autocompleteRef . current . contains ( e . target ) ) {
4158 setShowSuggestions ( false ) ;
59+ reset ( ) ;
4260 }
4361 }
4462
@@ -82,6 +100,7 @@ export const Autocomplete = (
82100 useKeybinding ( Key . ENTER , ( ) => {
83101 if ( showSuggestions && props . onItemClick ) {
84102 props . onItemClick ( curItems [ pos ] ) ;
103+ setShowSuggestions ( false ) ;
85104 return true ;
86105 }
87106 return false ;
@@ -109,6 +128,38 @@ export const Autocomplete = (
109128 delete trimmedProps . inputStyle ;
110129 delete trimmedProps . onItemClick ;
111130
131+ const [ inverted , setInverted ] = React . useState ( false ) ;
132+
133+ const checkDirection = ( ) => {
134+ if ( autocompleteRef && autocompleteRef . current && menuRef . current && ! ( event . target === menuRef . current ) ) {
135+ const node = inputRef . current ;
136+ if ( node && menuRef . current ) {
137+ const rect = node . getBoundingClientRect ( ) ;
138+ const computedStyle = window . getComputedStyle ( node ) ;
139+ const marginBottom = parseInt ( computedStyle . marginBottom , 10 ) || 0 ;
140+ let menuTop = rect . bottom + marginBottom ;
141+ if ( window . innerHeight - ( menuTop + menuRef . current . offsetHeight ) < 30 ) {
142+ if ( ! inverted ) {
143+ setInverted ( true ) ;
144+ }
145+ } else {
146+ if ( inverted ) {
147+ setInverted ( false ) ;
148+ }
149+ }
150+ }
151+ }
152+ } ;
153+
154+ React . useEffect ( ( ) => {
155+ document . addEventListener ( 'scroll' , checkDirection , true ) ;
156+ document . addEventListener ( 'resize' , checkDirection , true ) ;
157+ return ( ) => {
158+ document . removeEventListener ( 'scroll' , checkDirection ) ;
159+ document . removeEventListener ( 'resize' , checkDirection ) ;
160+ } ;
161+ } ) ;
162+
112163 return (
113164 < div className = 'autocomplete' ref = { autocompleteRef } style = { style as any } >
114165 < Input
@@ -121,24 +172,29 @@ export const Autocomplete = (
121172 props . onChange ( e ) ;
122173 }
123174 } }
124- onFocus = { ( ) => setShowSuggestions ( true ) }
175+ onFocus = { ( ) => {
176+ checkDirection ( ) ;
177+ setShowSuggestions ( true ) ;
178+ } }
125179 />
126- < ThemeDiv className = 'autocomplete__items' hidden = { ! showSuggestions || ( props . items || [ ] ) . length < 1 } >
127- { ( curItems || [ ] ) . map ( ( i , n ) => (
128- < div
129- key = { i }
130- onClick = { ( ) => {
131- if ( props . onItemClick ) {
132- props . onItemClick ( i ) ;
133- }
134- props . onChange ( { target : { value : i } } as React . ChangeEvent < HTMLInputElement > ) ;
135- setShowSuggestions ( false ) ;
136- } }
137- className = { `autocomplete__items__item ${ pos === n ? 'autocomplete__items__item--selected' : '' } ` } >
138- { i }
139- </ div >
140- ) ) }
141- </ ThemeDiv >
180+ < div ref = { menuRef } >
181+ < ThemeDiv className = { `autocomplete__items ${ inverted ? 'autocomplete__items--inverted' : '' } ` } hidden = { ! showSuggestions || ( props . items || [ ] ) . length < 1 } >
182+ { ( curItems || [ ] ) . map ( ( i , n ) => (
183+ < div
184+ key = { i }
185+ onClick = { ( ) => {
186+ props . onChange ( { target : { value : i } } as React . ChangeEvent < HTMLInputElement > ) ;
187+ setShowSuggestions ( false ) ;
188+ if ( props . onItemClick ) {
189+ props . onItemClick ( i ) ;
190+ }
191+ } }
192+ className = { `autocomplete__items__item ${ pos === n ? 'autocomplete__items__item--selected' : '' } ` } >
193+ { i }
194+ </ div >
195+ ) ) }
196+ </ ThemeDiv >
197+ </ div >
142198 </ div >
143199 ) ;
144200} ;
0 commit comments