Skip to content

Commit 71d1343

Browse files
committed
chore(utils): Update keyboard movement
These changes are to help with the Tab movement. Add a new `horizontal` state to the movement config which updates the default keyboard config to use `ArrowRight` and `ArrowLeft` instead of `ArrowDown` and `ArrowUp`. Add a `nextFocusIndex` param to the `onFocusChange` handler.
1 parent aa0d3cd commit 71d1343

File tree

4 files changed

+66
-13
lines changed

4 files changed

+66
-13
lines changed

packages/utils/src/keyboardMovement/KeyboardMovementProvider.tsx

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import type { ReactElement, ReactNode } from "react";
22
import { useMemo, useRef } from "react";
33

4+
import { useDir } from "../Dir";
45
import {
56
DEFAULT_KEYBOARD_MOVEMENT,
7+
DEFAULT_LTR_KEYBOARD_MOVEMENT,
8+
DEFAULT_RTL_KEYBOARD_MOVEMENT,
69
KeyboardMovementContextProvider,
710
} from "./movementContext";
811
import type {
@@ -36,9 +39,9 @@ export interface KeyboardMovementProviderProps
3639
* }
3740
*
3841
* function CustomKeyboardFocusWidget() {
39-
* const { onKeyDown } = useKeyboardFocus();
42+
* const { focusIndex: _focusIndex, ...eventHandlers } = useKeyboardFocus();
4043
* return (
41-
* <div onKeyDown={onKeyDown}>
44+
* <div {...eventHandlers}>
4245
* <FocusableChild />
4346
* <FocusableChild />
4447
* <FocusableChild />
@@ -60,12 +63,28 @@ export function KeyboardMovementProvider({
6063
children,
6164
loopable = false,
6265
searchable = false,
66+
horizontal = false,
6367
includeDisabled = false,
64-
incrementKeys = DEFAULT_KEYBOARD_MOVEMENT.incrementKeys,
65-
decrementKeys = DEFAULT_KEYBOARD_MOVEMENT.decrementKeys,
66-
jumpToFirstKeys = DEFAULT_KEYBOARD_MOVEMENT.jumpToFirstKeys,
67-
jumpToLastKeys = DEFAULT_KEYBOARD_MOVEMENT.jumpToLastKeys,
68+
incrementKeys: propIncrementKeys,
69+
decrementKeys: propDecrementKeys,
70+
jumpToFirstKeys: propJumpToFirstKeys,
71+
jumpToLastKeys: propJumpToLastKeys,
6872
}: KeyboardMovementProviderProps): ReactElement {
73+
const isRTL = useDir().dir === "rtl";
74+
let defaults: Readonly<Required<KeyboardMovementConfiguration>>;
75+
if (horizontal) {
76+
defaults = isRTL
77+
? DEFAULT_RTL_KEYBOARD_MOVEMENT
78+
: DEFAULT_LTR_KEYBOARD_MOVEMENT;
79+
} else {
80+
defaults = DEFAULT_KEYBOARD_MOVEMENT;
81+
}
82+
83+
const incrementKeys = propIncrementKeys || defaults.incrementKeys;
84+
const decrementKeys = propDecrementKeys || defaults.decrementKeys;
85+
const jumpToFirstKeys = propJumpToFirstKeys || defaults.jumpToFirstKeys;
86+
const jumpToLastKeys = propJumpToLastKeys || defaults.jumpToLastKeys;
87+
6988
const watching = useRef<KeyboardFocusElementData[]>([]);
7089
const configuration: KeyboardMovementConfig = {
7190
incrementKeys,
@@ -93,9 +112,10 @@ export function KeyboardMovementProvider({
93112
config,
94113
loopable,
95114
searchable,
115+
horizontal,
96116
includeDisabled: includeDisabled,
97117
}),
98-
[includeDisabled, loopable, searchable]
118+
[horizontal, includeDisabled, loopable, searchable]
99119
);
100120

101121
return (

packages/utils/src/keyboardMovement/movementContext.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ export const DEFAULT_KEYBOARD_MOVEMENT: Readonly<KeyboardMovementConfig> = {
2525
jumpToLastKeys: ["End"],
2626
};
2727

28+
/**
29+
* @remarks \@since 5.1.2
30+
* @internal
31+
*/
32+
export const DEFAULT_LTR_KEYBOARD_MOVEMENT: Readonly<KeyboardMovementConfig> = {
33+
incrementKeys: ["ArrowRight"],
34+
decrementKeys: ["ArrowLeft"],
35+
jumpToFirstKeys: ["Home"],
36+
jumpToLastKeys: ["End"],
37+
};
38+
39+
/**
40+
* @remarks \@since 5.1.2
41+
* @internal
42+
*/
43+
export const DEFAULT_RTL_KEYBOARD_MOVEMENT: Readonly<KeyboardMovementConfig> = {
44+
incrementKeys: ["ArrowLeft"],
45+
decrementKeys: ["ArrowRight"],
46+
jumpToFirstKeys: ["Home"],
47+
jumpToLastKeys: ["End"],
48+
};
49+
2850
/**
2951
* @remarks \@since 5.0.0
3052
* @internal
@@ -35,6 +57,7 @@ const context = createContext<KeyboardFocusContext>({
3557
watching: { current: [] },
3658
loopable: false,
3759
searchable: false,
60+
horizontal: false,
3861
includeDisabled: false,
3962
config: { current: DEFAULT_KEYBOARD_MOVEMENT },
4063
});

packages/utils/src/keyboardMovement/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ export interface KeyboardMovementBehavior {
7272
* @defaultValue `false`
7373
*/
7474
includeDisabled?: boolean;
75+
76+
/**
77+
* Boolean if the keyboard movement is horizontal instead of vertical. This
78+
* updates the default keyboard config to use `ArrowRight` and `ArrowLeft`
79+
* isntead of `ArrowDown` and `ArrowUp`,
80+
*
81+
* @remarks \@since 5.1.2
82+
* @defaultValue `false`
83+
*/
84+
horizontal?: boolean;
7585
}
7686

7787
/**

packages/utils/src/keyboardMovement/useKeyboardFocus.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ export interface KeyboardFocusHookOptions<E extends HTMLElement>
110110
* The default value is just to call `element.focus()`.
111111
*
112112
* @param element - The element that should gain custom focus
113+
* @param nextFocusIndex - The next focus index which can be used for
114+
* additional movement behavior.
113115
*/
114-
onFocusChange?(element: HTMLElement): void;
116+
onFocusChange?(element: HTMLElement, nextFocusIndex: number): void;
115117
}
116118

117-
/**
118-
* @remarks \@since 5.0.0
119-
*/
119+
/** @remarks \@since 5.0.0 */
120120
export interface KeyboardFocusHookReturnValue<E extends HTMLElement> {
121121
onFocus: FocusEventHandler<E>;
122122
onKeyDown: KeyboardEventHandler<E>;
@@ -184,7 +184,7 @@ export function useKeyboardFocus<E extends HTMLElement>(
184184

185185
focusIndex.current = defaultFocusIndex;
186186
const element = watching.current[focusIndex.current]?.element;
187-
element && onFocusChange(element);
187+
element && onFocusChange(element, focusIndex.current);
188188
},
189189
onKeyDown(event) {
190190
onKeyDown(event);
@@ -206,7 +206,7 @@ export function useKeyboardFocus<E extends HTMLElement>(
206206
focusIndex.current = index;
207207

208208
const element = watching.current[index]?.element;
209-
element && onFocusChange(element);
209+
element && onFocusChange(element, focusIndex.current);
210210
};
211211

212212
if (

0 commit comments

Comments
 (0)