import * as React from "react" ; import { shallowCompareKeys } from "../../common/utils" ; import { HotkeysDialog2 , HotkeysDialog2Props } from "../../components/hotkeys/hotkeysDialog2" ; import { HotkeyConfig } from "../../hooks" ; interface HotkeysContextState { /** List of hotkeys accessible in the current scope, registered by currently mounted components, can be global or local. */ hotkeys : HotkeyConfig [ ] ; /** Whether the global hotkeys dialog is open. */ isDialogOpen : boolean ; } type HotkeysAction = { type : "ADD_HOTKEYS" | "REMOVE_HOTKEYS" ; payload : HotkeyConfig [ ] ; } | { type : "CLOSE_DIALOG" | "OPEN_DIALOG" ; } ; export type HotkeysContextInstance = [ HotkeysContextState , React . Dispatch < HotkeysAction > ] ; const initialHotkeysState = { hotkeys : [ ] , isDialogOpen : false } ; const noOpDispatch = ( ) => null ; /**
 * A React context used to register and deregister hotkeys as components are mounted and unmounted in an application.
 * Users should take care to make sure that only _one_ of these is instantiated and used within an application, especially
 * if using global hotkeys.
 *
 * You will likely not be using this HotkeysContext directly, except in cases where you need to get a direct handle on an
 * exisitng context instance for advanced use cases involving nested HotkeysProviders.
 *
 * For more information, see the [HotkeysProvider documentation](https://blueprintjs.com/docs/#core/context/hotkeys-provider).
 */ export const HotkeysContext = React . createContext ?. < HotkeysContextInstance > ( [ initialHotkeysState , noOpDispatch ] ) ; const hotkeysReducer = ( state , action ) => { switch ( action . type ) { case "ADD_HOTKEYS" : const newUniqueHotkeys = [ ] ; for ( const a of action . payload ) { let isUnique = true ; for ( const b of state . hotkeys ) { isUnique &&= ! shallowCompareKeys ( a , b , { exclude : [ "onKeyDown" , "onKeyUp" ] } ) ; } if ( isUnique ) { newUniqueHotkeys . push ( a ) ; } } return { ... state , hotkeys : [ ... state . hotkeys , ... newUniqueHotkeys ] , } ; case "REMOVE_HOTKEYS" : return { ... state , hotkeys : state . hotkeys . filter ( key => action . payload . indexOf ( key ) === - 1 ) , } ; case "OPEN_DIALOG" : return { ... state , isDialogOpen : true } ; case "CLOSE_DIALOG" : return { ... state , isDialogOpen : false } ; default : return state ; } } ; export interface HotkeysProviderProps { /** The component subtree which will have access to this hotkeys context. */ children : React . ReactChild ; /** Optional props to customize the rendered hotkeys dialog. */ dialogProps ? : Partial < Omit < HotkeysDialog2Props , "hotkeys" > > ; /** If provided, this dialog render function will be used in place of the default implementation. */ renderDialog ? : ( state : HotkeysContextState , contextActions : { handleDialogClose : ( ) => void ; } ) => JSX . Element ; /** If provided, we will use this context instance instead of generating our own. */ value ? : HotkeysContextInstance ; } /**
 * Hotkeys context provider, necessary for the `useHotkeys` hook.
 */ export const HotkeysProvider = ( { children , dialogProps , renderDialog , value } ) => { const hasExistingContext = value != null ; const [ state , dispatch ] = value ?? React . useReducer ( hotkeysReducer , initialHotkeysState ) ; const handleDialogClose = React . useCallback ( ( ) => dispatch ( { type : "CLOSE_DIALOG" } ) , [ ] ) ; const dialog = renderDialog ?. ( state , { handleDialogClose } ) ?? ( < HotkeysDialog2 { ... dialogProps } isOpen = { state . isDialogOpen } hotkeys = { state . hotkeys } onClose = { handleDialogClose } / > ) ; return ( < HotkeysContext . Provider value = { [ state , dispatch ] } > { children } { hasExistingContext ? undefined : dialog } < /HotkeysContext.Provider>); } ;