@@ -6,69 +6,160 @@ import { Pagination } from 'react-instantsearch/dom';
66import { ENTER } from 'common/utils/keycodes' ;
77
88import DependencyHit from '../DependencyHit' ;
9- import { AutoCompleteInput } from './elements' ;
10-
11- function RawAutoComplete ( {
12- onSelect,
13- onManualSelect,
14- onHitVersionChange,
15- hits,
16- refine,
17- currentRefinement,
18- } ) {
19- return (
20- < Downshift itemToString = { hit => ( hit ? hit . name : hit ) } onSelect = { onSelect } >
21- { ( { getInputProps, getItemProps, highlightedIndex } ) => (
22- < div >
23- < AutoCompleteInput
24- autoFocus
25- { ...getInputProps ( {
26- innerRef ( ref ) {
27- if ( ref ) {
28- if (
29- document . activeElement &&
30- document . activeElement . tagName !== 'SELECT'
31- ) {
32- ref . focus ( ) ;
33- }
34- }
35- } ,
36- value : currentRefinement ,
37- placeholder : 'Search or enter npm dependency' ,
38- onChange ( e ) {
39- refine ( e . target . value ) ;
40- } ,
41- onKeyUp ( e ) {
42- // If enter with no selection
43- if ( e . keyCode === ENTER ) {
44- onManualSelect ( e . target . value ) ;
45- }
46- } ,
47- } ) }
48- />
49- < Pagination />
9+ import { AutoCompleteInput , SuggestionInput } from './elements' ;
10+
11+ /* eslint-disable no-param-reassign */
12+
13+ function getName ( value : string ) {
14+ const scope = value [ 0 ] === '@' ? '@' : '' ;
15+ value = scope ? value . substr ( 1 ) : value ;
16+
17+ return scope + value . split ( '@' ) [ 0 ] ;
18+ }
19+
20+ function isExplicitVersion ( value : string ) {
21+ const scope = value [ 0 ] === '@' ? '@' : '' ;
22+ value = scope ? value . substr ( 1 ) : value ;
23+
24+ return value . includes ( '@' ) ;
25+ }
26+
27+ function getVersion ( value : string , hit ) {
28+ return value . indexOf ( '@' ) > 0
29+ ? value . split ( '@' ) [ 1 ]
30+ : hit
31+ ? hit . version
32+ : null ;
33+ }
34+
35+ function getIsValid ( value : string , hit , version : string ) {
36+ return Boolean (
37+ hit &&
38+ hit . name . startsWith ( getName ( value ) ) &&
39+ ( version in hit . tags || version in hit . versions )
40+ ) ;
41+ }
42+
43+ function getHit ( value : string , hits ) {
44+ return value && hits . find ( hit => hit . name . startsWith ( value ) ) ;
45+ }
46+
47+ class RawAutoComplete extends React . Component {
48+ state = {
49+ value : '' ,
50+ } ;
51+
52+ render ( ) {
53+ const {
54+ onSelect,
55+ onManualSelect,
56+ onHitVersionChange,
57+ hits,
58+ refine,
59+ currentRefinement,
60+ } = this . props ;
61+
62+ const hit = getHit ( currentRefinement , hits ) ;
63+ const version = getVersion ( this . state . value , hit ) ;
64+ const isValid = getIsValid ( this . state . value , hit , version ) ;
5065
66+ return (
67+ < Downshift itemToString = { h => ( h ? h . name : h ) } onSelect = { onSelect } >
68+ { ( { getInputProps, getItemProps, highlightedIndex } ) => (
5169 < div >
52- { hits . map ( ( hit , index ) => (
53- < DependencyHit
54- key = { hit . name }
55- { ...getItemProps ( {
56- item : hit ,
57- index,
58- highlighted : highlightedIndex === index ,
59- hit,
60- // Downshift supplies onClick
61- onVersionChange ( version ) {
62- onHitVersionChange ( hit , version ) ;
63- } ,
64- } ) }
65- />
66- ) ) }
70+ { highlightedIndex == null && (
71+ < SuggestionInput as = "div" >
72+ { isExplicitVersion ( this . state . value )
73+ ? this . state . value
74+ : hit
75+ ? hit . name
76+ : currentRefinement }
77+ < span
78+ css = { {
79+ color : 'var(--color-white-3)' ,
80+ } }
81+ >
82+ { isExplicitVersion ( this . state . value )
83+ ? null
84+ : hit && isValid
85+ ? '@' + hit . version
86+ : null }
87+ </ span >
88+ </ SuggestionInput >
89+ ) }
90+ < AutoCompleteInput
91+ autoFocus
92+ { ...getInputProps ( {
93+ innerRef ( ref ) {
94+ if ( ref ) {
95+ if (
96+ document . activeElement &&
97+ document . activeElement . tagName !== 'SELECT'
98+ ) {
99+ ref . focus ( ) ;
100+ }
101+ }
102+ } ,
103+ value : this . state . value ,
104+ placeholder : 'Search or enter npm dependency' ,
105+
106+ onChange : e => {
107+ const name = e . target . value ;
108+
109+ this . setState ( { value : name } , ( ) => {
110+ if ( name . indexOf ( '@' ) === 0 ) {
111+ const parts = name . split ( '@' ) ;
112+
113+ refine ( `@${ parts [ 1 ] } ` ) ;
114+ return ;
115+ }
116+
117+ const parts = name . split ( '@' ) ;
118+
119+ requestAnimationFrame ( ( ) => {
120+ refine ( `${ parts [ 0 ] } ` ) ;
121+ } ) ;
122+ } ) ;
123+ } ,
124+
125+ onKeyUp : e => {
126+ // If enter with no selection
127+ if ( e . keyCode === ENTER ) {
128+ onManualSelect (
129+ isExplicitVersion ( this . state . value )
130+ ? e . target . value
131+ : hit && isValid
132+ ? hit . name + '@' + hit . version
133+ : e . target . value
134+ ) ;
135+ }
136+ } ,
137+ } ) }
138+ />
139+ < Pagination />
140+
141+ < div >
142+ { hits . map ( ( h , index ) => (
143+ < DependencyHit
144+ key = { h . name }
145+ { ...getItemProps ( {
146+ item : h ,
147+ index,
148+ highlighted : highlightedIndex === index ,
149+ hit : h ,
150+ // Downshift supplies onClick
151+ onVersionChange ( v ) {
152+ onHitVersionChange ( h , v ) ;
153+ } ,
154+ } ) }
155+ />
156+ ) ) }
157+ </ div >
67158 </ div >
68- </ div >
69- ) }
70- </ Downshift >
71- ) ;
159+ ) }
160+ </ Downshift >
161+ ) ;
162+ }
72163}
73164
74165export default RawAutoComplete ;
0 commit comments