@@ -10,60 +10,118 @@ import { useKeyboardNavigation } from "@hooks/useKeyboardNavigation";
1010import { useLanguages } from "@hooks/useLanguages" ;
1111import { LanguageType } from "@types" ;
1212import { configureUserSelection } from "@utils/configureUserSelection" ;
13+ import {
14+ getLanguageDisplayLogo ,
15+ getLanguageDisplayName ,
16+ } from "@utils/languageUtils" ;
1317import { slugify } from "@utils/slugify" ;
1418
1519import SubLanguageSelector from "./SubLanguageSelector" ;
1620
1721const LanguageSelector = ( ) => {
1822 const navigate = useNavigate ( ) ;
1923
20- const { language, setSearchText } = useAppContext ( ) ;
24+ const { language, subLanguage , setSearchText } = useAppContext ( ) ;
2125 const { fetchedLanguages, loading, error } = useLanguages ( ) ;
22- const allLanguages = useMemo (
23- ( ) =>
24- fetchedLanguages . flatMap ( ( lang ) =>
25- lang . subLanguages . length > 0
26- ? [
27- lang ,
28- ...lang . subLanguages . map ( ( subLang ) => ( {
29- ...subLang ,
30- mainLanguage : lang ,
31- subLanguages : [ ] ,
32- } ) ) ,
33- ]
34- : [ lang ]
35- ) ,
36- [ fetchedLanguages ]
37- ) ;
3826
3927 const dropdownRef = useRef < HTMLDivElement > ( null ) ;
40- const [ isOpen , setIsOpen ] = useState ( false ) ;
28+ const [ isOpen , setIsOpen ] = useState < boolean > ( false ) ;
4129 const [ openedLanguages , setOpenedLanguages ] = useState < LanguageType [ ] > ( [ ] ) ;
4230
31+ const keyboardItems = useMemo ( ( ) => {
32+ return fetchedLanguages . flatMap ( ( lang ) =>
33+ openedLanguages . map ( ( ol ) => ol . name ) . includes ( lang . name )
34+ ? [
35+ { languageName : lang . name } ,
36+ ...lang . subLanguages . map ( ( sl ) => ( {
37+ languageName : lang . name ,
38+ subLanguageName : sl . name ,
39+ } ) ) ,
40+ ]
41+ : [ { languageName : lang . name } ]
42+ ) ;
43+ } , [ fetchedLanguages , openedLanguages ] ) ;
44+
45+ const displayName = useMemo (
46+ ( ) => getLanguageDisplayName ( language . name , subLanguage ) ,
47+ [ language . name , subLanguage ]
48+ ) ;
49+
50+ const displayLogo = useMemo (
51+ ( ) => getLanguageDisplayLogo ( language . name , subLanguage ) ,
52+ [ language . name , subLanguage ]
53+ ) ;
54+
55+ const handleToggleSubLanguage = ( name : LanguageType [ "name" ] ) => {
56+ const isAlreadyOpened = openedLanguages . some ( ( lang ) => lang . name === name ) ;
57+ const openedLang = fetchedLanguages . find ( ( lang ) => lang . name === name ) ;
58+ if ( openedLang === undefined || openedLang . subLanguages . length === 0 ) {
59+ return ;
60+ }
61+
62+ if ( ! isAlreadyOpened ) {
63+ setOpenedLanguages ( ( prev ) => [ ...prev , openedLang ] ) ;
64+ } else {
65+ setOpenedLanguages ( ( prev ) =>
66+ prev . filter ( ( lang ) => lang . name !== openedLang . name )
67+ ) ;
68+ }
69+ } ;
70+
4371 /**
4472 * When setting a new language we need to ensure that a category
4573 * has been set given this new language.
4674 * Ensure that the search text is cleared.
4775 */
4876 const handleSelect = async ( selected : LanguageType ) => {
49- const { language : newLanguage , category : newCategory } =
50- await configureUserSelection ( {
51- languageName : selected . name ,
52- } ) ;
77+ const {
78+ language : newLanguage ,
79+ subLanguage : newSubLanguage ,
80+ category : newCategory ,
81+ } = await configureUserSelection ( {
82+ languageName : selected . name ,
83+ } ) ;
5384
5485 setSearchText ( "" ) ;
55- navigate ( `/${ slugify ( newLanguage . name ) } /${ slugify ( newCategory ) } ` ) ;
86+ navigate (
87+ `/${ slugify ( newLanguage . name ) } /${ slugify ( newSubLanguage ) } /${ slugify ( newCategory ) } `
88+ ) ;
5689 setIsOpen ( false ) ;
5790 setOpenedLanguages ( [ ] ) ;
5891 } ;
5992
93+ const afterSelect = ( ) => {
94+ setIsOpen ( false ) ;
95+ } ;
96+
97+ const handleSubLanguageSelect = async (
98+ selectedLanguageName : LanguageType [ "name" ] ,
99+ selectedSubLanguageName :
100+ | LanguageType [ "subLanguages" ] [ number ] [ "name" ]
101+ | undefined
102+ ) => {
103+ const {
104+ language : newLanguage ,
105+ subLanguage : newSubLanguage ,
106+ category : newCategory ,
107+ } = await configureUserSelection ( {
108+ languageName : selectedLanguageName ,
109+ subLanguageName : selectedSubLanguageName ,
110+ } ) ;
111+
112+ setSearchText ( "" ) ;
113+ navigate (
114+ `/${ slugify ( newLanguage . name ) } /${ slugify ( newSubLanguage ) } /${ slugify ( newCategory ) } `
115+ ) ;
116+ afterSelect ( ) ;
117+ } ;
118+
60119 const { focusedIndex, handleKeyDown, resetFocus, focusFirst } =
61120 useKeyboardNavigation ( {
62- items : allLanguages ,
121+ items : keyboardItems ,
63122 isOpen,
64- openedLanguages,
65- toggleDropdown : ( openedLang ) => handleToggleSublanguage ( openedLang ) ,
66- onSelect : handleSelect ,
123+ toggleDropdown : ( l ) => handleToggleSubLanguage ( l ) ,
124+ onSelect : ( l , sl ) => handleSubLanguageSelect ( l , sl ) ,
67125 onClose : ( ) => setIsOpen ( false ) ,
68126 } ) ;
69127
@@ -78,20 +136,6 @@ const LanguageSelector = () => {
78136 } , 0 ) ;
79137 } ;
80138
81- const handleToggleSublanguage = ( openedLang : LanguageType ) => {
82- const isAlreadyOpened = openedLanguages . some (
83- ( lang ) => lang . name === openedLang . name
84- ) ;
85-
86- if ( ! isAlreadyOpened ) {
87- setOpenedLanguages ( ( prev ) => [ ...prev , openedLang ] ) ;
88- } else {
89- setOpenedLanguages ( ( prev ) =>
90- prev . filter ( ( lang ) => lang . name !== openedLang . name )
91- ) ;
92- }
93- } ;
94-
95139 const toggleDropdown = ( ) => {
96140 setIsOpen ( ( prev ) => {
97141 if ( ! prev ) setTimeout ( focusFirst , 0 ) ;
@@ -106,13 +150,6 @@ const LanguageSelector = () => {
106150 // eslint-disable-next-line react-hooks/exhaustive-deps
107151 } , [ isOpen ] ) ;
108152
109- useEffect ( ( ) => {
110- if ( language . mainLanguage ) {
111- handleToggleSublanguage ( language . mainLanguage ) ;
112- }
113- // eslint-disable-next-line react-hooks/exhaustive-deps
114- } , [ language ] ) ;
115-
116153 useEffect ( ( ) => {
117154 if ( isOpen && focusedIndex >= 0 ) {
118155 const element = document . querySelector (
@@ -144,8 +181,8 @@ const LanguageSelector = () => {
144181 onClick = { toggleDropdown }
145182 >
146183 < div className = "selector__value" >
147- < img src = { language . icon } alt = "" />
148- < span > { language . name || "Select a language" } </ span >
184+ < img src = { displayLogo } alt = "" />
185+ < span > { displayName } </ span >
149186 </ div >
150187 < span className = "selector__arrow" />
151188 </ button >
@@ -159,13 +196,12 @@ const LanguageSelector = () => {
159196 { fetchedLanguages . map ( ( lang , index ) =>
160197 lang . subLanguages . length > 0 ? (
161198 < SubLanguageSelector
162- key = { index }
163- mainLanguage = { lang }
164- afterSelect = { ( ) => {
165- setIsOpen ( false ) ;
166- } }
199+ key = { lang . name }
167200 opened = { openedLanguages . includes ( lang ) }
168- onDropdownToggle = { handleToggleSublanguage }
201+ parentLanguage = { lang }
202+ onDropdownToggle = { handleToggleSubLanguage }
203+ handleParentSelect = { handleSelect }
204+ afterSelect = { afterSelect }
169205 />
170206 ) : (
171207 < li
0 commit comments