@@ -155,13 +155,45 @@ export function reset(): void {
155155}
156156
157157export function focusWords ( ) : void {
158- $ ( "#wordsInput" ) . trigger ( "focus" ) ;
158+ const wordsInput = document . querySelector < HTMLElement > ( "#wordsInput" ) ;
159+ wordsInput ?. blur ( ) ;
160+ wordsInput ?. focus ( {
161+ preventScroll : true ,
162+ } ) ;
163+ if ( TestState . isActive ) {
164+ keepWordsInputInTheCenter ( true ) ;
165+ } else {
166+ const typingTest = document . querySelector < HTMLElement > ( "#typingTest" ) ;
167+ Misc . scrollToCenterOrTop ( typingTest ) ;
168+ }
159169}
160170
161171export function blurWords ( ) : void {
162172 $ ( "#wordsInput" ) . trigger ( "blur" ) ;
163173}
164174
175+ export function keepWordsInputInTheCenter ( force = false ) : void {
176+ const wordsInput = document . querySelector < HTMLElement > ( "#wordsInput" ) ;
177+ const wordsWrapper = document . querySelector < HTMLElement > ( "#wordsWrapper" ) ;
178+ if ( ! wordsInput || ! wordsWrapper ) return ;
179+
180+ const wordsWrapperHeight = wordsWrapper . offsetHeight ;
181+ const windowHeight = window . innerHeight ;
182+
183+ // dont do anything if the wrapper can fit on screen
184+ if ( wordsWrapperHeight < windowHeight ) return ;
185+
186+ const wordsInputRect = wordsInput . getBoundingClientRect ( ) ;
187+ const wordsInputBelowCenter = wordsInputRect . top > windowHeight / 2 ;
188+
189+ // dont do anything if its above or at the center unless forced
190+ if ( ! wordsInputBelowCenter && ! force ) return ;
191+
192+ wordsInput . scrollIntoView ( {
193+ block : "center" ,
194+ } ) ;
195+ }
196+
165197export function getWordElement ( index : number ) : HTMLElement | null {
166198 const el = document . querySelector < HTMLElement > (
167199 `#words .word[data-wordindex='${ index } ']`
@@ -197,9 +229,8 @@ export function updateActiveElement(
197229
198230 activeWordTop = newActiveWord . offsetTop ;
199231
200- if ( ! initial && shouldUpdateWordsInputPosition ( ) ) {
201- void updateWordsInputPosition ( ) ;
202- }
232+ void updateWordsInputPosition ( ) ;
233+
203234 if ( ! initial && Config . tapeMode !== "off" ) {
204235 void scrollTape ( ) ;
205236 }
@@ -448,7 +479,7 @@ function updateWordWrapperClasses(): void {
448479
449480 updateWordsWidth ( ) ;
450481 updateWordsWrapperHeight ( true ) ;
451- updateWordsMargin ( updateWordsInputPosition , [ true ] ) ;
482+ updateWordsMargin ( updateWordsInputPosition , [ ] ) ;
452483}
453484
454485export function showWords ( ) : void {
@@ -478,65 +509,54 @@ export function appendEmptyWordElement(
478509 `<div class='word' data-wordindex='${ index } '><letter class='invisible'>_</letter></div>`
479510 ) ;
480511}
512+ let updateWordsInputPositionAnimationFrameId : null | number = null ;
513+ export async function updateWordsInputPosition ( ) : Promise < void > {
514+ if ( updateWordsInputPositionAnimationFrameId !== null ) {
515+ cancelAnimationFrame ( updateWordsInputPositionAnimationFrameId ) ;
516+ }
517+ updateWordsInputPositionAnimationFrameId = requestAnimationFrame ( async ( ) => {
518+ updateWordsInputPositionAnimationFrameId = null ;
519+ if ( ActivePage . get ( ) !== "test" ) return ;
520+ const currentLanguage = await JSONData . getCurrentLanguage ( Config . language ) ;
521+ const isLanguageRTL = currentLanguage . rightToLeft ;
481522
482- const posUpdateLangList = [ "japanese" , "chinese" , "korean" ] ;
483- function shouldUpdateWordsInputPosition ( ) : boolean {
484- const language = posUpdateLangList . some ( ( l ) => Config . language . startsWith ( l ) ) ;
485- return language || ( Config . mode !== "time" && Config . showAllLines ) ;
486- }
487-
488- export async function updateWordsInputPosition ( initial = false ) : Promise < void > {
489- if ( ActivePage . get ( ) !== "test" ) return ;
490-
491- const currentLanguage = await JSONData . getCurrentLanguage ( Config . language ) ;
492- const isLanguageRTL = currentLanguage . rightToLeft ;
493-
494- const el = document . querySelector < HTMLElement > ( "#wordsInput" ) ;
523+ const el = document . querySelector < HTMLElement > ( "#wordsInput" ) ;
495524
496- if ( ! el ) return ;
525+ if ( ! el ) return ;
497526
498- const activeWord = getActiveWordElement ( ) ;
499-
500- if ( ! activeWord ) {
501- el . style . top = "0px" ;
502- el . style . left = "0px" ;
503- return ;
504- }
527+ const activeWord = getActiveWordElement ( ) ;
505528
506- const computed = window . getComputedStyle ( activeWord ) ;
507- const activeWordMargin =
508- parseInt ( computed . marginTop ) + parseInt ( computed . marginBottom ) ;
529+ if ( ! activeWord ) {
530+ el . style . top = "0px" ;
531+ el . style . left = "0px" ;
532+ return ;
533+ }
509534
510- const letterHeight = convertRemToPixels ( Config . fontSize ) ;
511- const targetTop =
512- activeWord . offsetTop + letterHeight / 2 - el . offsetHeight / 2 + 1 ; //+1 for half of border
535+ const letterHeight = convertRemToPixels ( Config . fontSize ) ;
536+ const targetTop =
537+ activeWord . offsetTop + letterHeight / 2 - el . offsetHeight / 2 + 1 ; //+1 for half of border
513538
514- if ( Config . tapeMode !== "off" ) {
515- el . style . maxWidth = `${ 100 - Config . tapeMargin } %` ;
516- } else {
517- el . style . maxWidth = "" ;
518- }
519- if ( activeWord . offsetWidth < letterHeight ) {
520- el . style . width = letterHeight + "px" ;
521- } else {
522- el . style . width = activeWord . offsetWidth + "px" ;
523- }
539+ if ( Config . tapeMode !== "off" ) {
540+ el . style . maxWidth = `${ 100 - Config . tapeMargin } %` ;
541+ } else {
542+ el . style . maxWidth = "" ;
543+ }
544+ if ( activeWord . offsetWidth < letterHeight ) {
545+ el . style . width = letterHeight + "px" ;
546+ } else {
547+ el . style . width = activeWord . offsetWidth + "px" ;
548+ }
524549
525- if (
526- initial &&
527- ! shouldUpdateWordsInputPosition ( ) &&
528- Config . tapeMode === "off"
529- ) {
530- el . style . top = targetTop + letterHeight + activeWordMargin + 4 + "px" ;
531- } else {
532550 el . style . top = targetTop + "px" ;
533- }
534551
535- if ( activeWord . offsetWidth < letterHeight && isLanguageRTL ) {
536- el . style . left = activeWord . offsetLeft - letterHeight + "px" ;
537- } else {
538- el . style . left = Math . max ( 0 , activeWord . offsetLeft ) + "px" ;
539- }
552+ if ( activeWord . offsetWidth < letterHeight && isLanguageRTL ) {
553+ el . style . left = activeWord . offsetLeft - letterHeight + "px" ;
554+ } else {
555+ el . style . left = Math . max ( 0 , activeWord . offsetLeft ) + "px" ;
556+ }
557+
558+ keepWordsInputInTheCenter ( ) ;
559+ } ) ;
540560}
541561
542562let centeringActiveLine : Promise < void > = Promise . resolve ( ) ;
0 commit comments