Socket
Socket
Sign inDemoInstall

react-hotkeys-hook

Package Overview
Dependencies
Maintainers
1
Versions
111
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-hotkeys-hook - npm Package Compare versions

Comparing version 4.0.0-1 to 4.0.0-2

2

dist/index.js

@@ -1,2 +0,2 @@

import{useCallback as _,useLayoutEffect as j,useRef as B}from"react";var O=["ctrl","shift","alt","meta","mod"];function v(t,o=","){return typeof t=="string"?t.split(o):t}function H(t,o="+"){let e=t.toLocaleLowerCase().split(o).map(c=>c.trim()),y={alt:e.includes("alt"),ctrl:e.includes("ctrl"),shift:e.includes("shift"),meta:e.includes("meta"),mod:e.includes("mod")},i=e.filter(c=>!O.includes(c));return{...y,keys:i}}function b(t,o,e){(typeof e=="function"&&e(t,o)||e===!0)&&t.preventDefault()}function h(t,o,e){return typeof e=="function"?e(t,o):e===!0||e===void 0}function P(t){return g(t,["input","textarea","select"])}function g({target:t},o=!1){let e=t&&t.tagName;return o instanceof Array?Boolean(e&&o&&o.some(y=>y.toLowerCase()===e.toLowerCase())):Boolean(e&&o&&o===!0)}function E(t,o){return t.length===0&&o?(console.warn('A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>'),!0):o?t.some(e=>o.includes(e))||t.includes("*"):!0}var C=(t,o,e)=>{let{alt:y,ctrl:i,meta:c,mod:r,shift:p,keys:a}=o,{altKey:k,ctrlKey:d,metaKey:l,shiftKey:s,key:u,code:n}=t,m=n.toLowerCase().replace("key",""),f=u.toLowerCase();if(k!==y&&f!=="alt"||s!==p&&f!=="shift")return!1;if(r){if(!l&&!d)return!1}else if(l!==c&&m!=="meta"||d!==i&&m!=="ctrl")return!1;return a&&a.length===1&&(a.includes(f)||a.includes(m))?!0:a?a.every(M=>e.has(M)):!a};import{createContext as I,useMemo as N,useState as T,useContext as F}from"react";import{createContext as R,useContext as D}from"react";var S=R(void 0),w=()=>D(S);function x({addHotkey:t,removeHotkey:o,children:e}){return React.createElement(S.Provider,{value:{addHotkey:t,removeHotkey:o}},e)}var A=I({hotkeys:[],activeScopes:[],toggleScope:()=>{},activateScope:()=>{},deactivateScope:()=>{}}),K=()=>F(A),U=({initiallyActiveScopes:t=["*"],children:o})=>{let[e,y]=T(t?.length>0?t:["*"]),[i,c]=T([]),r=N(()=>e.includes("*"),[e]),p=s=>{y(r?[s]:Array.from(new Set([...e,s])))},a=s=>{let u=e.filter(n=>n!==s);u.length===0?y(["*"]):y(u)},k=s=>{e.includes(s)?a(s):p(s)},d=s=>{c([...i,s])},l=s=>{c(i.filter(u=>u.keys!==s.keys))};return React.createElement(A.Provider,{value:{activeScopes:e,hotkeys:i,activateScope:p,deactivateScope:a,toggleScope:k}},React.createElement(x,{addHotkey:d,removeHotkey:l},o))};function L(t,o,e,y){let i=B(null),{current:c}=B(new Set),r=e instanceof Array?y instanceof Array?void 0:y:e,p=e instanceof Array?e:y instanceof Array?y:[],a=_(o,[...p]),k=K(),d=w();return j(()=>{if(r?.enabled===!1||!E(k.activeScopes,r?.scopes))return;let l=n=>{P(n)&&!g(n,r?.enableOnFormTags)||i.current!==null&&document.activeElement!==i.current&&!i.current.contains(document.activeElement)||n.target?.isContentEditable&&!r?.enableOnContentEditable||v(t,r?.splitKey).forEach(m=>{let f=H(m,r?.combinationKey);if(C(n,f,c)||f.keys?.includes("*")){if(b(n,f,r?.preventDefault),!h(n,f,r?.enabled))return;a(n,f)}})},s=n=>{c.add(n.key.toLowerCase()),(r?.keydown===void 0&&r?.keyup!==!0||r?.keydown)&&l(n)},u=n=>{c.delete(n.key.toLowerCase()),r?.keyup&&l(n)};return(i.current||document).addEventListener("keyup",u),(i.current||document).addEventListener("keydown",s),d&&v(t,r?.splitKey).forEach(n=>d.addHotkey(H(n,r?.combinationKey))),()=>{(i.current||document).removeEventListener("keyup",u),(i.current||document).removeEventListener("keydown",s),d&&v(t,r?.splitKey).forEach(n=>d.removeHotkey(H(n,r?.combinationKey)))}},[t,a,r]),i}export{U as HotkeysProvider,L as useHotkeys,K as useHotkeysContext};
import{useCallback as j,useLayoutEffect as q,useRef as B}from"react";var O=["ctrl","shift","alt","meta","mod"];function v(t,o=","){return typeof t=="string"?t.split(o):t}function H(t,o="+"){let e=t.toLocaleLowerCase().split(o).map(c=>c.trim()),a={alt:e.includes("alt"),ctrl:e.includes("ctrl"),shift:e.includes("shift"),meta:e.includes("meta"),mod:e.includes("mod")},i=e.filter(c=>!O.includes(c));return{...a,keys:i}}function b(t,o,e){(typeof e=="function"&&e(t,o)||e===!0)&&t.preventDefault()}function P(t,o,e){return typeof e=="function"?e(t,o):e===!0||e===void 0}function h(t){return g(t,["input","textarea","select"])}function g({target:t},o=!1){let e=t&&t.tagName;return o instanceof Array?Boolean(e&&o&&o.some(a=>a.toLowerCase()===e.toLowerCase())):Boolean(e&&o&&o===!0)}function E(t,o){return t.length===0&&o?(console.warn('A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>'),!0):o?t.some(e=>o.includes(e))||t.includes("*"):!0}var C=(t,o,e)=>{let{alt:a,ctrl:i,meta:c,mod:n,shift:l,keys:y}=o,{altKey:k,ctrlKey:d,metaKey:p,shiftKey:s,key:u,code:r}=t,m=r.toLowerCase().replace("key",""),f=u.toLowerCase();if(k!==a&&f!=="alt"||s!==l&&f!=="shift")return!1;if(n){if(!p&&!d)return!1}else if(p!==c&&m!=="meta"||d!==i&&m!=="ctrl")return!1;return y&&y.length===1&&(y.includes(f)||y.includes(m))?!0:y?y.every(D=>e.has(D)):!y};import{createContext as N,useMemo as F,useState as T,useContext as U}from"react";import{createContext as R,useContext as I}from"react";var S=R(void 0),w=()=>I(S);function x({addHotkey:t,removeHotkey:o,children:e}){return React.createElement(S.Provider,{value:{addHotkey:t,removeHotkey:o}},e)}var A=N({hotkeys:[],activeScopes:[],toggleScope:()=>{},activateScope:()=>{},deactivateScope:()=>{}}),K=()=>U(A),_=({initiallyActiveScopes:t=["*"],children:o})=>{let[e,a]=T(t?.length>0?t:["*"]),[i,c]=T([]),n=F(()=>e.includes("*"),[e]),l=s=>{a(n?[s]:Array.from(new Set([...e,s])))},y=s=>{let u=e.filter(r=>r!==s);u.length===0?a(["*"]):a(u)},k=s=>{e.includes(s)?y(s):l(s)},d=s=>{c([...i,s])},p=s=>{c(i.filter(u=>u.keys!==s.keys))};return React.createElement(A.Provider,{value:{activeScopes:e,hotkeys:i,activateScope:l,deactivateScope:y,toggleScope:k}},React.createElement(x,{addHotkey:d,removeHotkey:p},o))};var L=t=>{t.stopPropagation(),t.preventDefault(),t.stopImmediatePropagation()};function M(t,o,e,a){let i=B(null),{current:c}=B(new Set),n=e instanceof Array?a instanceof Array?void 0:a:e,l=e instanceof Array?e:a instanceof Array?a:[],y=j(o,[...l]),k=K(),d=w();return q(()=>{if(n?.enabled===!1||!E(k.activeScopes,n?.scopes))return;let p=r=>{if(!(h(r)&&!g(r,n?.enableOnFormTags))){if(i.current!==null&&document.activeElement!==i.current&&!i.current.contains(document.activeElement)){L(r);return}r.target?.isContentEditable&&!n?.enableOnContentEditable||v(t,n?.splitKey).forEach(m=>{let f=H(m,n?.combinationKey);if(C(r,f,c)||f.keys?.includes("*")){if(b(r,f,n?.preventDefault),!P(r,f,n?.enabled)){L(r);return}y(r,f)}})}},s=r=>{c.add(r.key.toLowerCase()),(n?.keydown===void 0&&n?.keyup!==!0||n?.keydown)&&p(r)},u=r=>{c.delete(r.key.toLowerCase()),n?.keyup&&p(r)};return(i.current||document).addEventListener("keyup",u),(i.current||document).addEventListener("keydown",s),d&&v(t,n?.splitKey).forEach(r=>d.addHotkey(H(r,n?.combinationKey))),()=>{(i.current||document).removeEventListener("keyup",u),(i.current||document).removeEventListener("keydown",s),d&&v(t,n?.splitKey).forEach(r=>d.removeHotkey(H(r,n?.combinationKey)))}},[t,y,n]),i}export{_ as HotkeysProvider,M as useHotkeys,K as useHotkeysContext};
//# sourceMappingURL=index.js.map
{
"name": "react-hotkeys-hook",
"version": "4.0.0-1",
"version": "4.0.0-2",
"repository": "https://JohannesKlauss@github.com/JohannesKlauss/react-keymap-hook.git",

@@ -5,0 +5,0 @@ "homepage": "https://johannesklauss.github.io/react-hotkeys-hook/",

<hr>
<div align="center">
<h1 align="center">
useHotkeys(key, handler)
useHotkeys(keys, callback)
</h1>

@@ -32,3 +32,3 @@ </div>

<p align="center">
A React hook for using keyboard shortcuts in components.
A React hook for using keyboard shortcuts in components in a declarative way.
</p>

@@ -40,2 +40,4 @@

The easiest way to use the hook.
```jsx harmony

@@ -56,6 +58,69 @@ import { useHotkeys } from 'react-hotkeys-hook'

The hook takes care of all the binding and unbinding for you.
As soon as the component mounts into the DOM, the key stroke will be
listened to. When the component unmounts it will stop listening.
### Scopes
Scopes allow you to group hotkeys together. You can use scopes to prevent hotkeys from colliding with each other.
```jsx harmony
const App = () => {
return (
<HotkeysProvider initiallyActiveScopes={['settings']}>
<ExampleComponent />
</HotkeysProvider>
)
}
export const ExampleComponent = () => {
const [count, setCount] = useState(0)
useHotkeys('ctrl+k', () => setCount(prevCount => prevCount + 1), { scopes: ['settings'] })
return (
<p>
Pressed {count} times.
</p>
)
}
```
#### Changing a scope's active state
You can change the active state of a scope using the `deactivateScope`, `activateScope` and `toggleScope` functions
returned by the `useHotkeysContext()` hook. Note that you have to have your app wrapped in a `<HotkeysProvider>` component.
```jsx harmony
const App = () => {
return (
<HotkeysProvider initiallyActiveScopes={['settings']}>
<ExampleComponent />
</HotkeysProvider>
)
}
export const ExampleComponent = () => {
const { toggleScope } = useHotkeysContext()
return (
<button onClick={() => toggleScope('settings')}>
Change scope active state
</button>
)
}
```
### Focus trap
This will only trigger the hotkey if the component is focused.
```tsx harmony
export const ExampleComponent = () => {
const [count, setCount] = useState(0)
const ref = useHotkeys<HTMLParagraphElement>('ctrl+k', () => setCount(prevCount => prevCount + 1))
return (
<p tabIndex={-1} ref={ref}>
Pressed {count} times.
</p>
)
}
```
## Documentation & Live Examples

@@ -67,42 +132,44 @@

## [Join the discussion for version 4!](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/574)
If you use this package please share your thoughts on how we can improve this hook with version 4.
Please engage at the corresponding [Github issue](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/574).
## API
### useHotkeys()
### useHotkeys(keys, callback)
```typescript
useHotkeys(keys: string, callback: (event: KeyboardEvent, handler: HotkeysEvent) => void, options: Options = {}, deps: any[] = [])
useHotkeys(keys: string | string[], callback: (event: KeyboardEvent, handler: HotkeysEvent) => void, options: Options = {}, deps: DependencyList = [])
```
### Parameters
- `keys: string`: Here you can set the key strokes you want the hook to listen to. You can use single or multiple keys,
modifier combination, etc. See [this](https://github.com/jaywcjlove/hotkeys/#defining-shortcuts)
section on the hotkeys documentation for more info.
- `callback: (event: KeyboardEvent, handler: HotkeysEvent) => void`: Gets executed when the defined keystroke
gets hit by the user. **Important:** Since version 1.5.0 this callback gets memoised inside the hook. So you don't have
to do this anymore by yourself.
- `options: Options = {}`
- `filter: (event: KeyboardEvent): boolean` is used to filter if a callback gets triggered depending on the keyboard event.
**Breaking Change in `3.0.0`!** Prior to version `3.0.0` the filter settings was one global setting that applied to every
hook. Since `3.0.0` this behavior changed. The `filter` option is now locally scoped to each call of `useHotkeys`.
- `filterPreventDefault: boolean` is used to prevent/allow the default browser behavior for the keystroke when the filter return false (default value: `true`)
- `enableOnTags: string[]` is used to enable listening to hotkeys in form fields. Available values are `INPUT`, `TEXTAREA` and `SELECT`.
- `splitKey: string` is used to change the splitting character inside the keys argument. Default is `+`, but if you want
to listen to the `+` character, you can set `splitKey` to i.e. `-` and listen for `ctrl-+`
- `keyup: boolean` Determine if you want to listen on the keyup event
- `keydown: boolean` Determine if want to listen on the keydown event
- `enabled: boolean` is used to prevent installation of the hotkey when set to false (default value: `true`)
- `deps: any[] = []`: The dependency array that gets appended to the memoisation of the callback. Here you define the inner
dependencies of your callback. If for example your callback actions depend on a referentially unstable value or a value
that will change over time, you should add this value to your deps array. Since most of the time your callback won't
depend on any unstable callbacks or changing values over time you can leave this value alone since it will be set to an
empty array by default. See the [Memoisation](#memoisation) section to
learn more and see an example where you have to set this array.
| Parameter | Type | Required? | Default value | Description |
|---------------|---------------------------------------------------------|-----------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `keys` | `string` or `string[]` | required | - | set the hotkeys you want the hook to listen to. You can use single or multiple keys, modifier combinations, etc. This will either be a string or an array of strings. To separate multiple keys, use a colon. This split key value can be overridden with the `splitKey` option. |
| `callback` | `(event: KeyboardEvent, handler: HotkeysEvent) => void` | required | - | This is the callback function that will be called when the hotkey is pressed. The callback will receive the browsers native `KeyboardEvent` and the libraries `HotkeysEvent`. |
| `options` | `Options` | optional | `{}` | Object to modify the behavior of the hook. Default options are given below. |
| `dependencies` | `DependencyList` | optional | `[]` | The given callback will always be memoised inside the hook. So if you reference any outside variables, you need to set them here for the callback to get updated (Much like `useCallback` works in React). |
### `isHotkeyPressed` function
### Options
All options are optional and have a default value which you can override to change the behavior of the hook.
| Option | Type | Default value | Description |
|--------------------------|--------------------------------------------------------------------------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `enabled` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `true` | This option determines whether the hotkey is active or not. It can take a boolean (for example a flag from a state outside) or a function which gets executed once the hotkey is pressed. If the function returns `false` the hotkey won't get executed and all browser events are prevented. |
| `enableOnFormTags` | `boolean` or `FormTags[]` | `false` | By default hotkeys are not registered if a focus focuses on an input field. This will prevent accidental triggering of hotkeys when the user is typing. If you want to enable hotkeys, use this option. Setting it to true will enable on all form tags, otherwise you can give an array of form tags to enable the hotkey on (possible options are: `['input', 'textarea', 'select']`) |
| `enableOnContentEditable` | `boolean` | `false` | Set this option to enable hotkeys on tags that have set the `contentEditable` prop to `true` |
| `combinationKey` | `string` | `+` | Character to indicate keystrokes like `shift+c`. You might want to change this if you want to listen to the `+` character like `ctrl-+`. |
| `splitKey` | `string` | `,` | Character to separate different keystrokes like `ctrl+a, ctrl+b`. |
| `scopes` | `string` or `string[]` | `*` | With scopes you can group hotkeys together. The default scope is the wildcard `*` which matches all hotkeys. Use the `<HotkeysProvider>` component to change active scopes. |
| `keyup` | `boolean` | `false` | Determines whether to listen to the browsers `keyup` event for triggering the callback. |
| `keydown` | `boolean` | `true` | Determines whether to listen to the browsers `keydown` event for triggering the callback. If you set both `keyup`and `keydown` to true, the callback will trigger on both events. |
| `preventDefault` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `false` | Set this to a `true` if you want the hook to prevent the browsers default behavior on certain keystrokes like `meta+s` to save a page. NOTE: Certain keystrokes are not preventable, like `meta+w` to close a tab in chrome. |
| `description` | `string` | `undefined` | Use this option to describe what the hotkey does. this is helpful if you want to display a list of active hotkeys to the user. |
#### Overloads
The hooks call signature is very flexible. For example if you don't need to set any special options you can use the dependency
array as your third parameter:
`useHotkeys('ctrl+k', () => console.log(counter + 1), [counter])`
### `isHotkeyPressed(keys: string | string[], splitKey?: string = ',')`
This function allows us to check if the user is currently pressing down a key.

@@ -113,5 +180,11 @@

isHotkeyPressed('return') // Returns true if Return key is pressed down.
isHotkeyPressed('esc') // Returns true if Escape key is pressed down.
```
You can also check for multiple keys at the same time:
```ts
isHotkeyPressed(['esc', 'ctrl+s']) // Returns true if Escape or Ctrl+S are pressed down.
```
## Support

@@ -132,3 +205,3 @@

## Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

@@ -135,0 +208,0 @@ 1. Fork the Project

@@ -37,3 +37,3 @@ import type { DependencyList } from 'react'

keydown?: boolean // Trigger on keydown event? (Default: true)
preventDefault?: Trigger // Prevent default browser behavior? (Default: true)
preventDefault?: Trigger // Prevent default browser behavior? (Default: false)
description?: string // Use this option to describe what the hotkey does. (Default: undefined)

@@ -40,0 +40,0 @@ }

@@ -15,2 +15,8 @@ import { HotkeyCallback, Keys, OptionsOrDependencyArray, RefType } from './types'

const stopPropagation = (e: KeyboardEvent): void => {
e.stopPropagation()
e.preventDefault()
e.stopImmediatePropagation()
}
export default function useHotkeys<T extends HTMLElement>(

@@ -44,2 +50,4 @@ keys: Keys,

if (ref.current !== null && document.activeElement !== ref.current && !ref.current.contains(document.activeElement)) {
stopPropagation(e)
return

@@ -59,2 +67,4 @@ }

if (!isHotkeyEnabled(e, hotkey, _options?.enabled)) {
stopPropagation(e)
return

@@ -61,0 +71,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc