11'use client' ;
22
33import type { ReactElement } from 'react' ;
4- import { useState } from 'react' ;
4+ import { useState , useRef } from 'react' ;
55import { useForm } from 'react-hook-form' ;
66import { track } from '@amplitude/analytics-browser' ;
77import { MarkGithubIcon , SearchIcon } from '@primer/octicons-react' ;
@@ -17,8 +17,10 @@ import TextInput from '../../(common)/TextInput';
1717
1818import StatsSymbols from './StatsSymbol' ;
1919import StatsUrlCard from './StatsUrlCards' ;
20+ import SearchHistoryDropdown from './SearchHistoryDropdown' ;
2021import { useMediaQuery } from 'usehooks-ts' ;
2122import GreatFrontEnd from '../../(common)/GreatFrontEnd' ;
23+ import { useSearchHistory } from '../../../../src/hooks/useSearchHistory' ;
2224
2325const rootUrl = `${ process . env . NEXT_PUBLIC_ROOT_URL } /api` ;
2426const baseUrl = process . env . NEXT_PUBLIC_ROOT_URL ;
@@ -72,13 +74,20 @@ function Hero({t, statsInfo}: Props): ReactElement {
7274 const [ svgStatsURLDisplay , setSvgStatsURLDisplay ] = useState < string > ( '' ) ;
7375 const [ svgStatsURLCopy , setSvgStatsURLCopy ] = useState < string > ( '' ) ;
7476 const [ isBasic , setIsBasic ] = useState < boolean | undefined > ( undefined ) ;
77+ const [ showHistory , setShowHistory ] = useState ( false ) ;
7578 const { register, formState, handleSubmit} = useForm ( ) ;
79+ const { history, addToHistory, removeFromHistory} = useSearchHistory ( ) ;
80+ const searchContainerRef = useRef < HTMLDivElement > ( null ) ;
7681
7782 const searchUser = async ( ) : Promise < void > => {
7883 if ( selectedPluginType . domain === 'github.com' ) {
7984 try {
8085 const svgStats = await fetchGithubStats ( login ) ;
8186
87+ // Add to search history
88+ addToHistory ( login ) ;
89+ setShowHistory ( false ) ;
90+
8291 track ( 'Search User' , { login} ) ;
8392
8493 // NOTE: Should use `unescape` to translate chinese letters. The new api `decodeURIComponent` won't work.
@@ -100,6 +109,18 @@ function Hero({t, statsInfo}: Props): ReactElement {
100109 }
101110 } ;
102111
112+ const handleHistorySelect = ( item : string ) => {
113+ setLogin ( item ) ;
114+ setShowHistory ( false ) ;
115+ // Trigger search
116+ setTimeout ( ( ) => {
117+ const form = searchContainerRef . current ?. querySelector ( 'form' ) ;
118+ if ( form ) {
119+ form . requestSubmit ( ) ;
120+ }
121+ } , 100 ) ;
122+ } ;
123+
103124 return (
104125 < div
105126 className = { clsx (
@@ -129,67 +150,81 @@ function Hero({t, statsInfo}: Props): ReactElement {
129150 />
130151 { /* End: GreatFrontEnd Banner */ }
131152 { /* Begin: Search Form */ }
132- < form
133- onSubmit = { handleSubmit ( searchUser ) }
134- className = "self-stretch"
135- autoComplete = "off"
136- >
137- < div
138- className = { clsx (
139- 'rounded-[16px] px-3 h-[64px] relative body2 max-w-[800px]' ,
140- 'flex flex-row-reverse items-center' ,
141- 'bg-white/50 dark:bg-black/40' ,
142- 'backdrop-blur-2xl' ,
143- 'border border-black/10 dark:border-white/20' ,
144- 'shadow-[0_20px_60px_-15px_rgba(0,0,0,0.2)]' ,
145- 'hover:bg-white/60 dark:hover:bg-black/50' ,
146- 'transition-all duration-300' ,
147- 'max-[425px]:p-3 max-[425px]:self-stretch max-[425px]:h-auto max-[425px]:flex-wrap' ,
148- 'max-[320px]:py-3 max-[320px]:items-start' ,
149- ) }
153+ < div ref = { searchContainerRef } className = "relative self-stretch max-w-[800px] w-full" >
154+ < form
155+ onSubmit = { handleSubmit ( searchUser ) }
156+ className = "self-stretch w-full"
157+ autoComplete = "off"
150158 >
151- < Button
152- loading = { formState . isSubmitting }
153- type = "submit"
154- className = { clsx (
155- 'bg-transparent border-0 text-center max-w-[100px] p-2' ,
156- 'absolute' ,
157- ) }
158- text = { < SearchIcon size = { 22 } className = "text-gray8 dark:text-white" /> }
159- />
160159 < div
161160 className = { clsx (
162- 'flex-1' ,
163- 'flex flex-row items-center overflow-x-clip' ,
161+ 'rounded-[16px] px-3 h-[64px] relative body2 w-full' ,
162+ 'flex flex-row-reverse items-center' ,
163+ 'bg-white/50 dark:bg-black/40' ,
164+ 'backdrop-blur-2xl' ,
165+ 'border border-black/10 dark:border-white/20' ,
166+ 'shadow-[0_20px_60px_-15px_rgba(0,0,0,0.2)]' ,
167+ 'hover:bg-white/60 dark:hover:bg-black/50' ,
168+ 'transition-all duration-300' ,
169+ 'max-[425px]:p-3 max-[425px]:self-stretch max-[425px]:h-auto max-[425px]:flex-wrap' ,
170+ 'max-[320px]:py-3 max-[320px]:items-start' ,
164171 ) }
165172 >
166- < Dropdown
167- data = { statTypes }
168- selected = { selectedPluginType }
169- setSelected = { ( el ) => {
170- setSelectedPluginType ( el ) ;
171- } }
173+ < Button
174+ loading = { formState . isSubmitting }
175+ type = "submit"
176+ className = { clsx (
177+ 'bg-transparent border-0 text-center max-w-[100px] p-2' ,
178+ 'absolute' ,
179+ ) }
180+ text = { < SearchIcon size = { 22 } className = "text-gray8 dark:text-white" /> }
172181 />
173- < span
182+ < div
174183 className = { clsx (
175- 'text-gray5 dark:text-gray3' ,
176- 'mx-3 body3 text-[22px]' ,
177- 'max-[425px]:invisible max-[425px]:hidden' ,
184+ 'flex-1' ,
185+ 'flex flex-row items-center overflow-x-clip' ,
178186 ) }
179187 >
180- /
181- </ span >
182- < TextInput
183- className = "text-gray7 dark:text-white placeholder:text-gray5 dark:placeholder:text-gray4"
184- { ...register ( 'githubID' ) }
185- placeholder = { t . githubUsername }
186- onChange = { ( e ) => {
187- setLogin ( e . target . value . trim ( ) ) ;
188- } }
189- />
188+ < Dropdown
189+ data = { statTypes }
190+ selected = { selectedPluginType }
191+ setSelected = { ( el ) => {
192+ setSelectedPluginType ( el ) ;
193+ } }
194+ />
195+ < span
196+ className = { clsx (
197+ 'text-gray5 dark:text-gray3' ,
198+ 'mx-3 body3 text-[22px]' ,
199+ 'max-[425px]:invisible max-[425px]:hidden' ,
200+ ) }
201+ >
202+ /
203+ </ span >
204+ < TextInput
205+ className = "text-gray7 dark:text-white placeholder:text-gray5 dark:placeholder:text-gray4"
206+ placeholder = { t . githubUsername }
207+ value = { login }
208+ onChange = { ( e ) => {
209+ setLogin ( e . target . value . trim ( ) ) ;
210+ } }
211+ onFocus = { ( ) => setShowHistory ( true ) }
212+ onBlur = { ( ) => {
213+ // Delay to allow click on history items
214+ setTimeout ( ( ) => setShowHistory ( false ) , 200 ) ;
215+ } }
216+ />
217+ </ div >
190218 </ div >
191- </ div >
192- </ form >
219+ </ form >
220+ < SearchHistoryDropdown
221+ history = { history }
222+ query = { login }
223+ onSelectAction = { handleHistorySelect }
224+ onRemoveAction = { removeFromHistory }
225+ show = { showHistory }
226+ />
227+ </ div >
193228 { /* End: Search Form */ }
194229 { /* Begin: Stats */ }
195230 { githubSVG ? (
0 commit comments