12
12
* @module keyboard
13
13
*/
14
14
15
- import { IKeyboardLayout } from './core' ;
15
+ import { IKeyboardLayout , KeycodeLayout } from './core' ;
16
16
export { IKeyboardLayout , KeycodeLayout } from './core' ;
17
17
18
18
export { EN_US } from './layouts' ;
19
19
import { EN_US } from './layouts' ;
20
20
import * as Layouts from './layouts' ;
21
+ import { MODIFIER_KEYS } from './special-keys' ;
21
22
22
23
export const KeyboardLayouts = Object . values ( Layouts ) ;
23
24
@@ -42,9 +43,40 @@ export function getKeyboardLayout(): IKeyboardLayout {
42
43
* to a layout which is appropriate for the user's system.
43
44
*/
44
45
export function setKeyboardLayout ( layout : IKeyboardLayout ) : void {
46
+ try {
47
+ Private . unsubscribeBrowserUpdates ( ) ;
48
+ } catch ( e ) {
49
+ // Ignore exceptions in experimental code
50
+ }
45
51
Private . keyboardLayout = layout ;
46
52
}
47
53
54
+ /**
55
+ * Whether the browser supports inspecting the keyboard layout.
56
+ *
57
+ * @alpha
58
+ */
59
+ export function hasBrowserLayout ( ) : boolean {
60
+ return ! ! ( navigator as any ) ?. keyboard ?. getLayoutMap ;
61
+ }
62
+
63
+ /**
64
+ * Use the keyboard layout of the browser if it supports it.
65
+ *
66
+ * @alpha
67
+ * @returns Whether the browser supports inspecting the keyboard layout.
68
+ */
69
+ export async function useBrowserLayout ( ) : Promise < boolean > {
70
+ const keyboardApi = ( navigator as any ) ?. keyboard ;
71
+ if ( ! ( await Private . updateBrowserLayout ( ) ) ) {
72
+ return false ;
73
+ }
74
+ if ( keyboardApi ?. addEventListener ) {
75
+ keyboardApi . addEventListener ( 'layoutchange' , Private . updateBrowserLayout ) ;
76
+ }
77
+ return true ;
78
+ }
79
+
48
80
/**
49
81
* The namespace for the module implementation details.
50
82
*/
@@ -53,4 +85,61 @@ namespace Private {
53
85
* The global keyboard layout instance.
54
86
*/
55
87
export let keyboardLayout = EN_US ;
88
+
89
+ /**
90
+ * Polyfill until Object.fromEntries is available
91
+ */
92
+ function fromEntries < T > ( entries : Iterable < [ string , T ] > ) {
93
+ const ret = { } as { [ key : string ] : T } ;
94
+ for ( const [ key , value ] of entries ) {
95
+ ret [ key ] = value ;
96
+ }
97
+ return ret ;
98
+ }
99
+
100
+ /**
101
+ * Get the current browser keyboard layout, or null if unsupported.
102
+ *
103
+ * @returns The keyboard layout of the browser at this moment if supported, otherwise null.
104
+ */
105
+ export async function getBrowserKeyboardLayout ( ) : Promise <
106
+ IKeyboardLayout | undefined
107
+ > {
108
+ const keyboardApi = ( navigator as any ) ?. keyboard ;
109
+ if ( ! keyboardApi ) {
110
+ return undefined ;
111
+ }
112
+ const browserMap = await keyboardApi . getLayoutMap ( ) ;
113
+ if ( ! browserMap ) {
114
+ return undefined ;
115
+ }
116
+ return new KeycodeLayout (
117
+ 'browser' ,
118
+ { } ,
119
+ MODIFIER_KEYS ,
120
+ fromEntries ( browserMap . entries ( ) )
121
+ ) ;
122
+ }
123
+
124
+ /**
125
+ * Set the active layout to that of the browser at this instant.
126
+ */
127
+ export async function updateBrowserLayout ( ) : Promise < boolean > {
128
+ const initial = await getBrowserKeyboardLayout ( ) ;
129
+ if ( ! initial ) {
130
+ return false ;
131
+ }
132
+ keyboardLayout = initial ;
133
+ return true ;
134
+ }
135
+
136
+ /**
137
+ * Unsubscribe any browser updates
138
+ */
139
+ export function unsubscribeBrowserUpdates ( ) : void {
140
+ const keyboardApi = ( navigator as any ) ?. keyboard ;
141
+ if ( keyboardApi ?. removeEventListener ) {
142
+ keyboardApi . removeEventListener ( updateBrowserLayout ) ;
143
+ }
144
+ }
56
145
}
0 commit comments