@@ -10,60 +10,118 @@ import { useKeyboardNavigation } from "@hooks/useKeyboardNavigation";
10
10
import { useLanguages } from "@hooks/useLanguages" ;
11
11
import { LanguageType } from "@types" ;
12
12
import { configureUserSelection } from "@utils/configureUserSelection" ;
13
+ import {
14
+ getLanguageDisplayLogo ,
15
+ getLanguageDisplayName ,
16
+ } from "@utils/languageUtils" ;
13
17
import { slugify } from "@utils/slugify" ;
14
18
15
19
import SubLanguageSelector from "./SubLanguageSelector" ;
16
20
17
21
const LanguageSelector = ( ) => {
18
22
const navigate = useNavigate ( ) ;
19
23
20
- const { language, setSearchText } = useAppContext ( ) ;
24
+ const { language, subLanguage , setSearchText } = useAppContext ( ) ;
21
25
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
- ) ;
38
26
39
27
const dropdownRef = useRef < HTMLDivElement > ( null ) ;
40
- const [ isOpen , setIsOpen ] = useState ( false ) ;
28
+ const [ isOpen , setIsOpen ] = useState < boolean > ( false ) ;
41
29
const [ openedLanguages , setOpenedLanguages ] = useState < LanguageType [ ] > ( [ ] ) ;
42
30
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
+
43
71
/**
44
72
* When setting a new language we need to ensure that a category
45
73
* has been set given this new language.
46
74
* Ensure that the search text is cleared.
47
75
*/
48
76
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
+ } ) ;
53
84
54
85
setSearchText ( "" ) ;
55
- navigate ( `/${ slugify ( newLanguage . name ) } /${ slugify ( newCategory ) } ` ) ;
86
+ navigate (
87
+ `/${ slugify ( newLanguage . name ) } /${ slugify ( newSubLanguage ) } /${ slugify ( newCategory ) } `
88
+ ) ;
56
89
setIsOpen ( false ) ;
57
90
setOpenedLanguages ( [ ] ) ;
58
91
} ;
59
92
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
+
60
119
const { focusedIndex, handleKeyDown, resetFocus, focusFirst } =
61
120
useKeyboardNavigation ( {
62
- items : allLanguages ,
121
+ items : keyboardItems ,
63
122
isOpen,
64
- openedLanguages,
65
- toggleDropdown : ( openedLang ) => handleToggleSublanguage ( openedLang ) ,
66
- onSelect : handleSelect ,
123
+ toggleDropdown : ( l ) => handleToggleSubLanguage ( l ) ,
124
+ onSelect : ( l , sl ) => handleSubLanguageSelect ( l , sl ) ,
67
125
onClose : ( ) => setIsOpen ( false ) ,
68
126
} ) ;
69
127
@@ -78,20 +136,6 @@ const LanguageSelector = () => {
78
136
} , 0 ) ;
79
137
} ;
80
138
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
-
95
139
const toggleDropdown = ( ) => {
96
140
setIsOpen ( ( prev ) => {
97
141
if ( ! prev ) setTimeout ( focusFirst , 0 ) ;
@@ -106,13 +150,6 @@ const LanguageSelector = () => {
106
150
// eslint-disable-next-line react-hooks/exhaustive-deps
107
151
} , [ isOpen ] ) ;
108
152
109
- useEffect ( ( ) => {
110
- if ( language . mainLanguage ) {
111
- handleToggleSublanguage ( language . mainLanguage ) ;
112
- }
113
- // eslint-disable-next-line react-hooks/exhaustive-deps
114
- } , [ language ] ) ;
115
-
116
153
useEffect ( ( ) => {
117
154
if ( isOpen && focusedIndex >= 0 ) {
118
155
const element = document . querySelector (
@@ -144,8 +181,8 @@ const LanguageSelector = () => {
144
181
onClick = { toggleDropdown }
145
182
>
146
183
< 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 >
149
186
</ div >
150
187
< span className = "selector__arrow" />
151
188
</ button >
@@ -159,13 +196,12 @@ const LanguageSelector = () => {
159
196
{ fetchedLanguages . map ( ( lang , index ) =>
160
197
lang . subLanguages . length > 0 ? (
161
198
< SubLanguageSelector
162
- key = { index }
163
- mainLanguage = { lang }
164
- afterSelect = { ( ) => {
165
- setIsOpen ( false ) ;
166
- } }
199
+ key = { lang . name }
167
200
opened = { openedLanguages . includes ( lang ) }
168
- onDropdownToggle = { handleToggleSublanguage }
201
+ parentLanguage = { lang }
202
+ onDropdownToggle = { handleToggleSubLanguage }
203
+ handleParentSelect = { handleSelect }
204
+ afterSelect = { afterSelect }
169
205
/>
170
206
) : (
171
207
< li
0 commit comments