@@ -11,6 +11,9 @@ type EventListenerTarget = {
1111} ;
1212
1313export function createInputManager ( editor : Editor , container : HTMLElement , dialog : DialogState , document : PortfolioState , fullscreen : FullscreenState ) : ( ) => void {
14+ const app = window . document . querySelector ( "[data-app]" ) as HTMLElement | undefined ;
15+ app ?. focus ( ) ;
16+
1417 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1518 const listeners : { target : EventListenerTarget ; eventName : EventName ; action : ( event : any ) => void ; options ?: boolean | AddEventListenerOptions } [ ] = [
1619 { target : window , eventName : "resize" , action : ( ) : void => onWindowResize ( container ) } ,
@@ -27,10 +30,20 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
2730 { target : window , eventName : "wheel" , action : ( e : WheelEvent ) : void => onMouseScroll ( e ) , options : { passive : false } } ,
2831 { target : window , eventName : "modifyinputfield" , action : ( e : CustomEvent ) : void => onModifyInputField ( e ) } ,
2932 { target : window . document . body , eventName : "paste" , action : ( e : ClipboardEvent ) : void => onPaste ( e ) } ,
33+ {
34+ target : app as EventListenerTarget ,
35+ eventName : "blur" ,
36+ action : ( ) : void => blurApp ( ) ,
37+ } ,
3038 ] ;
3139
3240 let viewportPointerInteractionOngoing = false ;
3341 let textInput = undefined as undefined | HTMLDivElement ;
42+ let canvasFocused = true ;
43+
44+ function blurApp ( ) : void {
45+ canvasFocused = false ;
46+ }
3447
3548 // Keyboard events
3649
@@ -66,8 +79,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
6679 if ( e . ctrlKey && e . shiftKey && key === "j" ) return false ;
6780
6881 // Don't redirect tab or enter if not in canvas (to allow navigating elements)
69- const inCanvas = e . target instanceof Element && e . target . closest ( "[data-canvas]" ) ;
70- if ( ! inCanvas && [ "tab" , "enter" , " " , "arrowdown" , "arrowup" , "arrowleft" , "arrowright" ] . includes ( key . toLowerCase ( ) ) ) return false ;
82+ if ( ! canvasFocused && [ "tab" , "enter" , " " , "arrowdown" , "arrowup" , "arrowleft" , "arrowright" ] . includes ( key . toLowerCase ( ) ) ) return false ;
7183
7284 // Redirect to the backend
7385 return true ;
@@ -113,13 +125,20 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
113125 const inFloatingMenu = e . target instanceof Element && e . target . closest ( "[data-floating-menu-content]" ) ;
114126 if ( ! viewportPointerInteractionOngoing && inFloatingMenu ) return ;
115127
128+ const { target } = e ;
129+ const newInCanvas = ( target instanceof Element && target . closest ( "[data-canvas]" ) ) instanceof Element && ! targetIsTextField ( window . document . activeElement ) ;
130+ if ( newInCanvas && ! canvasFocused ) {
131+ canvasFocused = true ;
132+ app ?. focus ( ) ;
133+ }
134+
116135 const modifiers = makeKeyboardModifiersBitfield ( e ) ;
117136 editor . instance . on_mouse_move ( e . clientX , e . clientY , e . buttons , modifiers ) ;
118137 }
119138
120139 function onPointerDown ( e : PointerEvent ) : void {
121140 const { target } = e ;
122- const inCanvas = target instanceof Element && target . closest ( "[data-canvas]" ) ;
141+ const isTargetingCanvas = target instanceof Element && target . closest ( "[data-canvas]" ) ;
123142 const inDialog = target instanceof Element && target . closest ( "[data-dialog-modal] [data-floating-menu-content]" ) ;
124143 const inTextInput = target === textInput ;
125144
@@ -131,7 +150,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
131150
132151 if ( ! inTextInput ) {
133152 if ( textInput ) editor . instance . on_change_text ( textInputCleanup ( textInput . innerText ) ) ;
134- else if ( inCanvas ) viewportPointerInteractionOngoing = true ;
153+ else viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element ;
135154 }
136155
137156 if ( viewportPointerInteractionOngoing ) {
@@ -168,7 +187,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
168187
169188 function onMouseScroll ( e : WheelEvent ) : void {
170189 const { target } = e ;
171- const inCanvas = target instanceof Element && target . closest ( "[data-canvas]" ) ;
190+ const isTargetingCanvas = target instanceof Element && target . closest ( "[data-canvas]" ) ;
172191
173192 // Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
174193 // There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
@@ -178,7 +197,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
178197 return ;
179198 }
180199
181- if ( inCanvas ) {
200+ if ( isTargetingCanvas ) {
182201 e . preventDefault ( ) ;
183202 const modifiers = makeKeyboardModifiersBitfield ( e ) ;
184203 editor . instance . on_mouse_scroll ( e . clientX , e . clientY , e . buttons , e . deltaX , e . deltaY , e . deltaZ , modifiers ) ;
@@ -246,7 +265,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
246265 } ) ;
247266 }
248267
249- function targetIsTextField ( target : EventTarget | null ) : boolean {
268+ function targetIsTextField ( target : EventTarget | HTMLElement | null ) : boolean {
250269 return target instanceof HTMLElement && ( target . nodeName === "INPUT" || target . nodeName === "TEXTAREA" || target . isContentEditable ) ;
251270 }
252271
0 commit comments