use-keybinds
Version:
Lightweight keybind library for React
1 lines • 8.68 kB
Source Map (JSON)
{"version":3,"sources":["../src/useKeybinds.tsx","../src/helpers/useFocusedElement.tsx","../src/KeybindsProvider.tsx","../src/utils.ts"],"sourcesContent":["import { useContext, useEffect } from \"react\";\r\nimport useFocusedElement from \"./helpers/useFocusedElement\";\r\nimport { KeybindsContext } from \"./KeybindsProvider\";\r\nimport type { KeybindCallbacks, KeyboardEventKeycode } from \"./types\";\r\nimport { createKeybindCombinationString } from \"./utils\";\r\n\r\nconst useKeybinds = <Slug extends string>(callbacks: KeybindCallbacks<Slug> = {}) => {\r\n const focusedElement = useFocusedElement();\r\n const { combinationsToKeybindKey } = useContext(KeybindsContext);\r\n\r\n useEffect(() => {\r\n const pressedKeyCodes = new Set<KeyboardEventKeycode>([]);\r\n const recentlyPressedKeyCodes = new Set<KeyboardEventKeycode>([]);\r\n\r\n const resetState = () => {\r\n pressedKeyCodes.clear();\r\n recentlyPressedKeyCodes.clear();\r\n };\r\n\r\n const isKeyCodeUsed = (keyCode: KeyboardEventKeycode) => {\r\n const allCombinationsString = Object.keys(combinationsToKeybindKey).join(\"\");\r\n return allCombinationsString.includes(keyCode);\r\n };\r\n\r\n const handleKeydown = (event: KeyboardEvent) => {\r\n // Block the event if the user is focusing a blacklisted element\r\n if (focusedElement && [\"INPUT\", \"TEXTAREA\"].includes(focusedElement.tagName)) {\r\n return;\r\n }\r\n\r\n const keyCode = event.code as KeyboardEventKeycode;\r\n\r\n if (isKeyCodeUsed(keyCode)) {\r\n pressedKeyCodes.add(keyCode);\r\n recentlyPressedKeyCodes.add(keyCode);\r\n setTimeout(() => recentlyPressedKeyCodes.delete(keyCode), 4000);\r\n } else {\r\n resetState();\r\n }\r\n\r\n const combinationString = createKeybindCombinationString([...pressedKeyCodes], false);\r\n const sequentialCombinationString = createKeybindCombinationString(\r\n [...recentlyPressedKeyCodes],\r\n true,\r\n );\r\n const keybindSlug = combinationsToKeybindKey[combinationString] as Slug;\r\n const sequentialKeybindSlug = combinationsToKeybindKey[sequentialCombinationString] as Slug;\r\n\r\n if (sequentialKeybindSlug) resetState();\r\n\r\n const callbackFunction = callbacks[sequentialKeybindSlug ?? keybindSlug];\r\n if (!callbackFunction) return;\r\n\r\n recentlyPressedKeyCodes.clear();\r\n callbackFunction(event);\r\n };\r\n\r\n const handleKeyup = (e: KeyboardEvent) => {\r\n // Block the event if the user is focusing a blacklisted element\r\n if (focusedElement && [\"INPUT\", \"TEXTAREA\"].includes(focusedElement.tagName)) {\r\n return;\r\n }\r\n\r\n const keyCode = e.code as KeyboardEventKeycode;\r\n pressedKeyCodes.delete(keyCode);\r\n };\r\n\r\n addEventListener(\"keydown\", handleKeydown);\r\n addEventListener(\"keyup\", handleKeyup);\r\n\r\n return () => {\r\n removeEventListener(\"keydown\", handleKeydown);\r\n removeEventListener(\"keyup\", handleKeyup);\r\n };\r\n }, [callbacks, combinationsToKeybindKey, focusedElement]);\r\n};\r\n\r\nexport default useKeybinds;\r\n","import { useCallback, useEffect, useState } from \"react\";\r\n\r\nconst useFocusedElement = (): Element | null => {\r\n const [focusedElement, setFocusedElement] = useState<Element | null>(\r\n typeof window !== \"undefined\" ? document.activeElement : null,\r\n );\r\n\r\n const handleFocusIn = useCallback(() => {\r\n setFocusedElement(document.activeElement);\r\n }, []);\r\n\r\n const handleFocusOut = useCallback(() => {\r\n setFocusedElement(null);\r\n }, []);\r\n\r\n useEffect(() => {\r\n addEventListener(\"focusin\", handleFocusIn);\r\n addEventListener(\"focusout\", handleFocusOut);\r\n return () => {\r\n removeEventListener(\"focusin\", handleFocusIn);\r\n removeEventListener(\"focusout\", handleFocusOut);\r\n };\r\n }, [handleFocusIn, handleFocusOut]);\r\n\r\n return focusedElement;\r\n};\r\n\r\nexport default useFocusedElement;\r\n","import React, { createContext, useMemo } from \"react\";\r\nimport { createKeybindCombinationString } from \"./utils\";\r\nimport type { ReactNode } from \"react\";\r\nimport type { Keybind, KeybindCombination, Keybinds } from \"./types\";\r\n\r\nexport const KeybindsContext = createContext<{\r\n keybinds: Keybinds<string>;\r\n combinationsToKeybindKey: Record<string, string>;\r\n}>({\r\n keybinds: {},\r\n combinationsToKeybindKey: {},\r\n});\r\n\r\ninterface KeybindsProviderProps<Slug extends string> {\r\n children: ReactNode;\r\n keybinds: Keybinds<Slug>;\r\n}\r\n\r\nconst KeybindsProvider = <Slug extends string>(props: KeybindsProviderProps<Slug>) => {\r\n const { children, keybinds = {} } = props;\r\n\r\n const combinationsToKeybindKey = useMemo<Record<string, Slug>>(() => {\r\n return Object.entries(keybinds).reduce((result, entry) => {\r\n const key = entry[0] as string;\r\n const keybind: Keybind = entry[1] as Keybind;\r\n\r\n const combinations: KeybindCombination[] = Array.isArray(keybind.keybind[0])\r\n ? (keybind.keybind as KeybindCombination[])\r\n : [keybind.keybind as KeybindCombination];\r\n\r\n const combinationStrings = combinations.reduce((result, combination) => {\r\n const combinationString = createKeybindCombinationString(\r\n combination,\r\n keybind.isSequential,\r\n );\r\n return { ...result, [combinationString]: key };\r\n }, {});\r\n\r\n return { ...result, ...combinationStrings };\r\n }, {});\r\n }, [keybinds]);\r\n\r\n return (\r\n <KeybindsContext.Provider value={{ keybinds, combinationsToKeybindKey }}>\r\n {children}\r\n </KeybindsContext.Provider>\r\n );\r\n};\r\n\r\nexport default KeybindsProvider;\r\n","import type { KeybindCombination } from \"./types\";\r\n\r\nexport const createKeybindCombinationString = (combination: KeybindCombination, isSequential?: boolean) => {\r\n return (isSequential ? \"isSequential-\" : \"\") + combination.join(\"\");\r\n};\r\n"],"mappings":";AAAA,OAAS,cAAAA,EAAY,aAAAC,MAAiB,QCAtC,OAAS,eAAAC,EAAa,aAAAC,EAAW,YAAAC,MAAgB,QAEjD,IAAMC,EAAoB,IAAsB,CAC9C,GAAM,CAACC,EAAgBC,CAAiB,EAAIH,EAC1C,OAAO,OAAW,IAAc,SAAS,cAAgB,IAC3D,EAEMI,EAAgBN,EAAY,IAAM,CACtCK,EAAkB,SAAS,aAAa,CAC1C,EAAG,CAAC,CAAC,EAECE,EAAiBP,EAAY,IAAM,CACvCK,EAAkB,IAAI,CACxB,EAAG,CAAC,CAAC,EAEL,OAAAJ,EAAU,KACR,iBAAiB,UAAWK,CAAa,EACzC,iBAAiB,WAAYC,CAAc,EACpC,IAAM,CACX,oBAAoB,UAAWD,CAAa,EAC5C,oBAAoB,WAAYC,CAAc,CAChD,GACC,CAACD,EAAeC,CAAc,CAAC,EAE3BH,CACT,EAEOI,EAAQL,EC3Bf,OAAOM,GAAS,iBAAAC,EAAe,WAAAC,MAAe,QCEvC,IAAMC,EAAiC,CAACC,EAAiCC,KACtEA,EAAe,gBAAkB,IAAMD,EAAY,KAAK,EAAE,EDE7D,IAAME,EAAkBC,EAG5B,CACD,SAAU,CAAC,EACX,yBAA0B,CAAC,CAC7B,CAAC,EFLD,IAAMC,EAAc,CAAsBC,EAAoC,CAAC,IAAM,CACnF,IAAMC,EAAiBC,EAAkB,EACnC,CAAE,yBAAAC,CAAyB,EAAIC,EAAWC,CAAe,EAE/DC,EAAU,IAAM,CACd,IAAMC,EAAkB,IAAI,IAA0B,CAAC,CAAC,EAClDC,EAA0B,IAAI,IAA0B,CAAC,CAAC,EAE1DC,EAAa,IAAM,CACvBF,EAAgB,MAAM,EACtBC,EAAwB,MAAM,CAChC,EAEME,EAAiBC,GACS,OAAO,KAAKR,CAAwB,EAAE,KAAK,EAAE,EAC9C,SAASQ,CAAO,EAGzCC,EAAiBC,GAAyB,CAE9C,GAAIZ,GAAkB,CAAC,QAAS,UAAU,EAAE,SAASA,EAAe,OAAO,EACzE,OAGF,IAAMU,EAAUE,EAAM,KAElBH,EAAcC,CAAO,GACvBJ,EAAgB,IAAII,CAAO,EAC3BH,EAAwB,IAAIG,CAAO,EACnC,WAAW,IAAMH,EAAwB,OAAOG,CAAO,EAAG,GAAI,GAE9DF,EAAW,EAGb,IAAMK,EAAoBC,EAA+B,CAAC,GAAGR,CAAe,EAAG,EAAK,EAC9ES,EAA8BD,EAClC,CAAC,GAAGP,CAAuB,EAC3B,EACF,EACMS,EAAcd,EAAyBW,CAAiB,EACxDI,EAAwBf,EAAyBa,CAA2B,EAE9EE,GAAuBT,EAAW,EAEtC,IAAMU,EAAmBnB,EAAUkB,GAAyBD,CAAW,EAClEE,IAELX,EAAwB,MAAM,EAC9BW,EAAiBN,CAAK,EACxB,EAEMO,EAAeC,GAAqB,CAExC,GAAIpB,GAAkB,CAAC,QAAS,UAAU,EAAE,SAASA,EAAe,OAAO,EACzE,OAGF,IAAMU,EAAUU,EAAE,KAClBd,EAAgB,OAAOI,CAAO,CAChC,EAEA,wBAAiB,UAAWC,CAAa,EACzC,iBAAiB,QAASQ,CAAW,EAE9B,IAAM,CACX,oBAAoB,UAAWR,CAAa,EAC5C,oBAAoB,QAASQ,CAAW,CAC1C,CACF,EAAG,CAACpB,EAAWG,EAA0BF,CAAc,CAAC,CAC1D,EAEOqB,EAAQvB","names":["useContext","useEffect","useCallback","useEffect","useState","useFocusedElement","focusedElement","setFocusedElement","handleFocusIn","handleFocusOut","useFocusedElement_default","React","createContext","useMemo","createKeybindCombinationString","combination","isSequential","KeybindsContext","createContext","useKeybinds","callbacks","focusedElement","useFocusedElement_default","combinationsToKeybindKey","useContext","KeybindsContext","useEffect","pressedKeyCodes","recentlyPressedKeyCodes","resetState","isKeyCodeUsed","keyCode","handleKeydown","event","combinationString","createKeybindCombinationString","sequentialCombinationString","keybindSlug","sequentialKeybindSlug","callbackFunction","handleKeyup","e","useKeybinds_default"]}