@@ -171,6 +171,11 @@ class TypingAnimation {
171171 this . changeHandler = null ;
172172 this . focusHandler = null ;
173173 this . blurHandler = null ;
174+ this . select2OpenHandler = null ;
175+ this . select2CloseHandler = null ;
176+ this . select2SelectionElement = null ;
177+ this . select2Container = null ;
178+ this . isSelect2 = false ;
174179 this . currentIndex = 0 ;
175180 this . currentText = '' ;
176181 this . isDeleting = false ;
@@ -187,11 +192,18 @@ class TypingAnimation {
187192 }
188193
189194 this . cleanup ( ) ;
195+ this . isSelect2 = this . selectElement . classList . contains ( 'select2-hidden-accessible' ) ;
196+ this . select2Container = this . selectElement . nextElementSibling && this . selectElement . nextElementSibling . classList . contains ( 'select2' )
197+ ? this . selectElement . nextElementSibling
198+ : null ;
199+ this . select2SelectionElement = this . select2Container
200+ ? this . select2Container . querySelector ( '.select2-selection' )
201+ : null ;
190202 this . setupHandlers ( ) ;
191203 this . checkSelection ( ) ;
192204
193205 this . initTimeoutId = setTimeout ( ( ) => {
194- if ( ! this . selectElement . value && document . activeElement !== this . selectElement && ! this . selectElement . disabled ) {
206+ if ( ! this . selectElement . value && ! this . isFocused ( ) && ! this . selectElement . disabled ) {
195207 this . typingElement . style . display = 'block' ;
196208 this . typingElement . textContent = '|' ;
197209 this . typeCharacter ( ) ;
@@ -220,6 +232,20 @@ class TypingAnimation {
220232 if ( this . blurHandler ) {
221233 this . selectElement . removeEventListener ( 'blur' , this . blurHandler ) ;
222234 }
235+ if ( this . select2OpenHandler && window . jQuery ) {
236+ window . jQuery ( this . selectElement ) . off ( 'select2:open' , this . select2OpenHandler ) ;
237+ }
238+ if ( this . select2CloseHandler && window . jQuery ) {
239+ window . jQuery ( this . selectElement ) . off ( 'select2:close' , this . select2CloseHandler ) ;
240+ }
241+ if ( this . select2SelectionElement ) {
242+ if ( this . focusHandler ) {
243+ this . select2SelectionElement . removeEventListener ( 'focus' , this . focusHandler ) ;
244+ }
245+ if ( this . blurHandler ) {
246+ this . select2SelectionElement . removeEventListener ( 'blur' , this . blurHandler ) ;
247+ }
248+ }
223249 }
224250
225251 if ( this . typingElement ) {
@@ -230,6 +256,41 @@ class TypingAnimation {
230256 this . currentIndex = 0 ;
231257 }
232258
259+ isFocused ( ) {
260+ if ( ! this . selectElement ) return false ;
261+ if ( this . isSelect2 && this . select2Container ) {
262+ if ( this . select2Container . classList . contains ( 'select2-container--open' ) ) {
263+ return true ;
264+ }
265+ return this . select2Container . contains ( document . activeElement ) ;
266+ }
267+ return document . activeElement === this . selectElement ;
268+ }
269+
270+ getDisplayText ( name ) {
271+ if ( name === null || name === undefined ) {
272+ return '' ;
273+ }
274+ if ( typeof name === 'string' || typeof name === 'number' ) {
275+ return String ( name ) ;
276+ }
277+ if ( typeof name === 'object' ) {
278+ if ( typeof name . text === 'string' || typeof name . text === 'number' ) {
279+ return String ( name . text ) ;
280+ }
281+ if ( typeof name . name === 'string' || typeof name . name === 'number' ) {
282+ return String ( name . name ) ;
283+ }
284+ if ( typeof name . label === 'string' || typeof name . label === 'number' ) {
285+ return String ( name . label ) ;
286+ }
287+ if ( typeof name . id === 'string' || typeof name . id === 'number' ) {
288+ return String ( name . id ) ;
289+ }
290+ }
291+ return '' ;
292+ }
293+
233294 setupHandlers ( ) {
234295 this . changeHandler = ( ) => this . checkSelection ( ) ;
235296 this . focusHandler = ( ) => {
@@ -249,6 +310,16 @@ class TypingAnimation {
249310 this . selectElement . addEventListener ( 'change' , this . changeHandler ) ;
250311 this . selectElement . addEventListener ( 'focus' , this . focusHandler ) ;
251312 this . selectElement . addEventListener ( 'blur' , this . blurHandler ) ;
313+ if ( this . isSelect2 && window . jQuery ) {
314+ this . select2OpenHandler = ( ) => this . focusHandler ( ) ;
315+ this . select2CloseHandler = ( ) => this . blurHandler ( ) ;
316+ window . jQuery ( this . selectElement ) . on ( 'select2:open' , this . select2OpenHandler ) ;
317+ window . jQuery ( this . selectElement ) . on ( 'select2:close' , this . select2CloseHandler ) ;
318+ }
319+ if ( this . select2SelectionElement ) {
320+ this . select2SelectionElement . addEventListener ( 'focus' , this . focusHandler ) ;
321+ this . select2SelectionElement . addEventListener ( 'blur' , this . blurHandler ) ;
322+ }
252323 }
253324 }
254325
@@ -270,7 +341,7 @@ class TypingAnimation {
270341 clearTimeout ( this . timeoutId ) ;
271342 this . timeoutId = null ;
272343 }
273- } else if ( document . activeElement !== this . selectElement ) {
344+ } else if ( ! this . isFocused ( ) ) {
274345 this . typingElement . style . display = 'block' ;
275346 if ( ! this . timeoutId ) {
276347 this . currentText = '' ;
@@ -291,7 +362,12 @@ class TypingAnimation {
291362 return ;
292363 }
293364
294- const currentName = names [ this . currentIndex ] ;
365+ const currentName = this . getDisplayText ( names [ this . currentIndex ] ) ;
366+ if ( ! currentName ) {
367+ this . currentIndex = ( this . currentIndex + 1 ) % names . length ;
368+ this . timeoutId = setTimeout ( ( ) => this . typeCharacter ( ) , PAUSE_AFTER_DELETE ) ;
369+ return ;
370+ }
295371
296372 if ( this . selectElement . value ) {
297373 if ( this . typingElement ) {
@@ -1233,48 +1309,46 @@ async function loadAvailableGeographies(pathogen = '', preservedGeography = '')
12331309 }
12341310
12351311 const data = await response . json ( ) ;
1236- geographySelect . innerHTML = '<option value=""></option>' ;
1312+ console . log ( data . available_geos ) ;
1313+
12371314
12381315 if ( data && data . available_geos ) {
1239- const geos = data . available_geos ;
1240- let allGeoNames = [ ] ;
1241-
1242- geos . forEach ( group => {
1243- const optgroup = document . createElement ( 'optgroup' ) ;
1244- optgroup . label = group . text ;
1245-
1246- if ( group . children && Array . isArray ( group . children ) ) {
1247- group . children . forEach ( child => {
1248- const option = document . createElement ( 'option' ) ;
1249- option . value = child . id ;
1250- option . textContent = child . text ;
1251- optgroup . appendChild ( option ) ;
1252-
1253- allGeoNames . push ( child . text ) ;
1254- } ) ;
1255- }
1256-
1257- geographySelect . appendChild ( optgroup ) ;
1316+ geographySelect . innerHTML = '<option value=""></option>' ;
1317+
1318+ $ ( "#geographySelect" ) . select2 ( {
1319+ data : data . available_geos ,
1320+ minimumInputLength : 0 ,
1321+ maximumSelectionLength : 5 ,
1322+ width : '100%' ,
1323+ placeholder : '' ,
1324+ allowClear : false ,
12581325 } ) ;
1326+ $ ( "#geographySelect" ) . val ( '' ) . trigger ( 'change.select2' ) ;
12591327
1260- if ( allGeoNames . length > 0 ) {
1261- // Randomize names for typing animation
1262- // Optimized: Partial shuffle to get just 50 random items
1263- const count = Math . min ( 50 , allGeoNames . length ) ;
1264- for ( let i = 0 ; i < count ; i ++ ) {
1265- const j = i + Math . floor ( Math . random ( ) * ( allGeoNames . length - i ) ) ;
1266- [ allGeoNames [ i ] , allGeoNames [ j ] ] = [ allGeoNames [ j ] , allGeoNames [ i ] ] ;
1267- }
1268- window . geographyNames = allGeoNames . slice ( 0 , count ) ;
1328+ // Randomize names for typing animation
1329+ // Optimized: Partial shuffle to get just 50 random items
1330+ const count = Math . min ( 50 , data . available_geos . length ) ;
1331+ for ( let i = 0 ; i < count ; i ++ ) {
1332+ const j = i + Math . floor ( Math . random ( ) * ( data . available_geos . length - i ) ) ;
1333+ [ data . available_geos [ i ] , data . available_geos [ j ] ] = [ data . available_geos [ j ] , data . available_geos [ i ] ] ;
12691334 }
1335+ window . geographyNames = data . available_geos . slice ( 0 , count ) ;
12701336
12711337 if ( preservedGeography ) {
1272- const optionExists = Array . from ( geographySelect . options ) . some ( opt => opt . value === preservedGeography ) ;
1273- if ( optionExists ) {
1274- geographySelect . value = preservedGeography ;
1275- if ( typeof handleGeographyChange === 'function' ) {
1276- handleGeographyChange ( ) ;
1277- }
1338+ const hasGeoOption = ( items , id ) => {
1339+ if ( ! Array . isArray ( items ) ) return false ;
1340+ return items . some ( item => {
1341+ if ( ! item ) return false ;
1342+ if ( item . id === id ) return true ;
1343+ if ( Array . isArray ( item . children ) ) {
1344+ return hasGeoOption ( item . children , id ) ;
1345+ }
1346+ return false ;
1347+ } ) ;
1348+ } ;
1349+
1350+ if ( hasGeoOption ( data . available_geos , preservedGeography ) ) {
1351+ $ ( "#geographySelect" ) . val ( preservedGeography ) . trigger ( 'change' ) ;
12781352 }
12791353 }
12801354 }
0 commit comments