1212 * @module keyboard
1313 */
1414
15- import { IKeyboardLayout } from './core' ;
15+ import { IKeyboardLayout , KeycodeLayout } from './core' ;
1616export { IKeyboardLayout , KeycodeLayout } from './core' ;
1717
1818export { EN_US } from './layouts' ;
1919import { EN_US } from './layouts' ;
2020import * as Layouts from './layouts' ;
21+ import { MODIFIER_KEYS } from './special-keys' ;
2122
2223export const KeyboardLayouts = Object . values ( Layouts ) ;
2324
@@ -42,9 +43,40 @@ export function getKeyboardLayout(): IKeyboardLayout {
4243 * to a layout which is appropriate for the user's system.
4344 */
4445export function setKeyboardLayout ( layout : IKeyboardLayout ) : void {
46+ try {
47+ Private . unsubscribeBrowserUpdates ( ) ;
48+ } catch ( e ) {
49+ // Ignore exceptions in experimental code
50+ }
4551 Private . keyboardLayout = layout ;
4652}
4753
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+
4880/**
4981 * The namespace for the module implementation details.
5082 */
@@ -53,4 +85,61 @@ namespace Private {
5385 * The global keyboard layout instance.
5486 */
5587 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+ }
56145}
0 commit comments