1- import { getCurrentUrl , Link , route } from 'preact-router'
1+ import { getCurrentUrl , Link } from 'preact-router'
2+ import { useCallback , useMemo , useRef , useState } from 'preact/hooks'
3+ import type { ConfigGenerator } from '../Config.js'
24import config from '../Config.js'
35import { useLocale , useTheme , useTitle , useVersion } from '../contexts/index.js'
4- import { checkVersion } from '../services/index .js'
6+ import { useFocus } from '../hooks/useFocus .js'
57import { cleanUrl , getGenerator , SOURCE_REPO_URL } from '../Utils.js'
68import { Btn , BtnMenu , Icons , Octicon } from './index.js'
79
@@ -14,22 +16,16 @@ const Themes: Record<string, keyof typeof Octicon> = {
1416export function Header ( ) {
1517 const { lang, locale, changeLocale : changeLanguage } = useLocale ( )
1618 const { theme, changeTheme } = useTheme ( )
17- const { version } = useVersion ( )
1819 const { title } = useTitle ( )
1920 const url = getCurrentUrl ( )
2021 const gen = getGenerator ( url )
2122
2223 return < header >
23- < div class = "title" >
24- < Link class = "home-link" href = "/" aria-label = { locale ( 'home' ) } > { Icons . home } </ Link >
25- < h1 class = "font-bold" > { title } </ h1 >
26- { gen && < BtnMenu icon = "chevron_down" tooltip = { locale ( 'switch_generator' ) } >
27- { config . generators
28- . filter ( g => g . tags ?. [ 0 ] === gen ?. tags ?. [ 0 ] && checkVersion ( version , g . minVersion ) )
29- . map ( g =>
30- < Btn label = { locale ( `generator.${ g . id } ` ) } active = { g . id === gen . id } onClick = { ( ) => route ( cleanUrl ( g . url ) ) } />
31- ) }
32- </ BtnMenu > }
24+ < div class = "title flex items-center" >
25+ < Link class = "home-link pr-1" href = "/" aria-label = { locale ( 'home' ) } > { Icons . home } </ Link >
26+ { gen
27+ ? < GeneratorTitle title = { title } gen = { gen } />
28+ : < h1 class = "font-bold px-1 text-lg sm:text-2xl" > { title } </ h1 > }
3329 </ div >
3430 < nav >
3531 < ul >
@@ -58,3 +54,60 @@ export function Header() {
5854 </ nav >
5955 </ header >
6056}
57+
58+ interface GeneratorTitleProps {
59+ title : string
60+ gen : ConfigGenerator
61+ }
62+ function GeneratorTitle ( { title, gen } : GeneratorTitleProps ) {
63+ const { locale } = useLocale ( )
64+ const { version } = useVersion ( )
65+
66+ const [ active , setActive ] = useFocus ( )
67+ const [ search , setSearch ] = useState ( '' )
68+ const inputRef = useRef < HTMLInputElement > ( null )
69+
70+ const icon = Object . keys ( Icons ) . includes ( gen . id ) ? gen . id as keyof typeof Icons : undefined
71+
72+ const generators = useMemo ( ( ) => {
73+ let result = config . generators
74+ . filter ( g => ! g . dependency )
75+ . map ( g => ( { ...g , name : locale ( `generator.${ g . id } ` ) . toLowerCase ( ) } ) )
76+ if ( search ) {
77+ const parts = search . split ( ' ' )
78+ result = result . filter ( g => parts . some ( p => g . name . includes ( p ) )
79+ || parts . some ( p => g . tags ?. some ( t => t . includes ( p ) ) ?? false ) )
80+ }
81+ result . sort ( ( a , b ) => a . name . localeCompare ( b . name ) )
82+ if ( search ) {
83+ result . sort ( ( a , b ) => ( b . name . startsWith ( search ) ? 1 : 0 ) - ( a . name . startsWith ( search ) ? 1 : 0 ) )
84+ }
85+ return result
86+ } , [ locale , version , search ] )
87+
88+ const open = useCallback ( ( ) => {
89+ setActive ( true )
90+ setTimeout ( ( ) => {
91+ inputRef . current ?. select ( )
92+ } )
93+ } , [ setActive , inputRef ] )
94+
95+ return < div class = "px-1 relative" >
96+ < h1 class = "font-bold flex items-center cursor-pointer text-lg sm:text-2xl" onClick = { open } >
97+ { title }
98+ { icon && Icons [ icon ] }
99+ </ h1 >
100+ < div class = { `gen-menu absolute flex flex-col gap-2 p-2 rounded-lg drop-shadow-xl ${ active ? '' : 'hidden' } ` } >
101+ < input ref = { inputRef } type = "text" class = "py-1 px-2 w-full rounded" value = { search } placeholder = { locale ( 'generators.search' ) } onInput = { ( e ) => setSearch ( ( e . target as HTMLInputElement ) . value ) } onClick = { e => e . stopPropagation ( ) } />
102+ { active && < div class = "gen-results overflow-y-auto overscroll-none flex flex-col pr-2 h-96 max-h-max min-w-max" >
103+ { generators . length === 0 && < span class = "note" > { locale ( 'generators.no_results' ) } </ span > }
104+ { generators . map ( g =>
105+ < Link class = "flex items-center cursor-pointer no-underline rounded p-1" href = { cleanUrl ( g . url ) } onClick = { ( ) => setActive ( false ) } >
106+ { locale ( `generator.${ g . id } ` ) }
107+ { Object . keys ( Icons ) . includes ( g . id ) ? Icons [ g . id as keyof typeof Icons ] : undefined }
108+ </ Link >
109+ ) }
110+ </ div > }
111+ </ div >
112+ </ div >
113+ }
0 commit comments