A light and performant React implementation for @arrow-navigation/core package.
npm install --save @arrow-navigation/react
# or
yarn add @arrow-navigation/react
import React from 'react'
import {
initArrowNavigation,
FocusableElement,
FocusableGroup
} from '@arrow-navigation/react'
initArrowNavigation()
const App = () => {
return (
<div>
<FocusableGroup id="group-1">
<FocusableElement id="btn-1">
Button 1
</FocusableElement>
<FocusableElement id="btn-2">
Button 2
</FocusableElement>
<FocusableElement id="btn-3">
Button 3
</FocusableElement>
</FocusableGroup>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Initializes the arrow navigation. This function should be called only once in your application.
This hook returns the Arrow Navigation instance. It can be used to access the Arrow Navigation API from any part of your application.
import { useArrowNavigation } from '@arrow-navigation/react'
const navigationApi = useArrowNavigation()
A wrapper component that groups a set of focusable elements.
The FocusableGroup
component will receive all HTML attributes and events for the HTML tag used as wrapper. Some important props are:
Name | Type | Default | Description |
---|---|---|---|
id | string | - | The group id. Must be unique |
as | string | div | The HTML tag to be used as wrapper |
onFocus | function | - | Callback function to be called when the group receives focus. It returns a object with focus result that includes prev, current and direction |
onBlur | function | - | Callback function to be called when the group loses focus. It returns a object with focus result that includes next, current and direction |
firstElement | string | - | The id of the first element to receive focus when the group receives focus |
nextUp / nextDown / nextRight / nextLeft | string | - | The next group id by direction. |
saveLast | boolean | false | If true, the last focused element will be saved and used as firstElement |
viewportSafe | boolean | true | If true, the navigation will be limited to the viewport |
threshold | number | 0 | The threshold to intersection discriminator |
keepFocus | boolean | false | If true, the focus will be kept in the group when the last element receives focus |
byOrder | ORDER | 'horizontal' | Navigate by order setted on elements. Can be 'horizontal', 'vertical' or 'grid', this enum comes with ArrowNavigationOrder constant object. Take care with this option, because this will change the id of the elements, for example, for group-0, the element in order 1 will be group-0-1. It includes a utility function getElementIdByOrder(groupId, order): string. Keep this in mind if you are using the id of the elements for firstElement or nextByDirection options. |
cols | number - { number: number } | 1 | The number of columns to navigate when the byOrder is 'grid'. The default value is 1 and you can set a object with the number of columns for each breakpoint. For example: { 0: 1, 768: 2, 1024: 3 } |
A wrapper component that makes an element focusable. The element must be a child of a FocusableGroup
component and the HTML tag used as wrapper must be focusable, for example, input
, button
, a
, select
, textarea
, etc.
The FocusableElement
component will receive all HTML attributes and events for the HTML tag used as wrapper. Some important props are:
Name | Type | Default | Description |
---|---|---|---|
id | string | - | The element id. Must be unique |
as | string | div | The HTML tag to be used as wrapper |
onFocus | function | - | Callback function to be called when the element receives focus. It returns a object with focus result that includes prev, current and direction |
onBlur | function | - | Callback function to be called when the element loses focus. It returns a object with focus result that includes next, current and direction |
nextUp / nextDown / nextRight / nextLeft | string | NextFocusable | The next element or group by direction. In the case of group, must be an object { kind: 'group', id: 'group-0 } |
order | number | - | The order of the element. No default value. This is needed when the group is setted to navigate byOrder. If no setted, byOrder will be ignored. |
Hook to make an element focusable. The element must be a child of a FocusableGroup
component and the HTML tag used as wrapper must be focusable, for example, input
, button
, a
, select
, textarea
, etc. The hook props are the same as FocusableElement
component.
import { useFocusableElement } from '@arrow-navigation/react'
const MyButton = ({ id }) => {
// Is important to pass the id to the hook and to the button
useFocusableElement({ id })
return (
<button id={id}>
Button 1
</button>
)
}
const App = () => {
return (
<div>
<FocusableGroup id="group-1">
<MyButton id="btn-1" />
<MyButton id="btn-2" />
<MyButton id="btn-3" />
</FocusableGroup>
</div>
)
}
The @arrow-navigation/react
package also provides some listeners to help you to implement your own navigation and complex behaviors.
Hook to listen when an element receives focus and trigger a callback function.
import { useListenElementFocused } from '@arrow-navigation/react'
const App = () => {
useListenElementFocused(() => {
console.log('Button 1 focused')
}, 'btn-1')
return (
<div>
<FocusableGroup id="group-1">
<FocusableElement id="btn-1">
Button 1
</FocusableElement>
<FocusableElement id="btn-2">
Button 2
</FocusableElement>
</FocusableGroup>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Name | Type | Default | Description |
---|---|---|---|
callback | function | - | Callback function to be called when the element receives focus |
id | string | - | The element id |
Hook to listen when the last element of a group receives focus and trigger a callback function.
import { useListenLastElementReached } from '@arrow-navigation/react'
const App = () => {
useListenLastElementReached(() => {
console.log('Last element reached')
}, 'right')
return (
<div>
<FocusableGroup id="group-1">
<FocusableElement id="btn-1">
Button 1
</FocusableElement>
<FocusableElement id="btn-2">
Button 2
</FocusableElement>
</FocusableGroup>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Name | Type | Default | Description |
---|---|---|---|
callback | function | - | Callback function to be called when the last element of a group receives focus |
direction | string | - | The direction to navigate when the last element of a group receives focus. Possible values are: 'up', 'down', 'left' and 'right' |
options | object | - | The options for the listener. |
options.group | string | - | The group id. If not provided, the listener will be triggered for all groups |
options.inGroup | boolean | false | If true, the listener will be triggered only when the last element of the group receives focus |
options.elementPattern | RegExp | - | The pattern to match the element id. If not provided, the listener will be triggered for all elements |
Hook to listen when the last group of a group receives focus and trigger a callback function.
import { useListenLastGroupReached } from '@arrow-navigation/react'
const App = () => {
useListenLastGroupReached(() => {
console.log('Last group reached')
}, 'right')
return (
<div>
<FocusableGroup id="group-1">
<FocusableElement id="btn-1">
Button 1
</FocusableElement>
<FocusableElement id="btn-2">
Button 2
</FocusableElement>
</FocusableGroup>
<FocusableGroup id="group-2">
<FocusableElement id="btn-3">
Button 3
</FocusableElement>
<FocusableElement id="btn-4">
Button 4
</FocusableElement>
</FocusableGroup>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Name | Type | Default | Description |
---|---|---|---|
callback | function | - | Callback function to be called when the last group of a group receives focus |
direction | string | - | The direction to navigate when the last group of a group receives focus. Possible values are: 'up', 'down', 'left' and 'right' |
options | object | - | The options for the listener. |
options.groupPattern | RegExp | - | The pattern to match the group id. If not provided, the listener will be triggered for all groups |
The @arrow-navigation/react
package also provides some watchers to help you to implement your own navigation and complex behaviors. The watchers are similar to the listeners, but they will subscribe a state and make it available to be used in your application. Take care will the watchers, because they can cause multiple renders and performance issues if not used properly.
Hook to watch when an element receives focus and return a boolean state.
import { useWatchElementFocused } from '@arrow-navigation/react'
const App = () => {
const isBtn1Focused = useWatchElementFocused('btn-1')
return (
<div>
<FocusableGroup id="group-1">
<FocusableElement id="btn-1">
Button 1
</FocusableElement>
<FocusableElement id="btn-2">
Button 2
</FocusableElement>
</FocusableGroup>
<div>
{isBtn1Focused && <span>Button 1 is focused</span>}
</div>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Name | Type | Default | Description |
---|---|---|---|
id | string | - | The element id |