7ff2bc3e40
* Add shared roving grid navigation provider * Add RovingTabIndex stories and update RovingGridIndex stories * Handle multiple toolbars/grids in on roving group * Refactor EmojiPicker to use shared roving grid navigation * Exclude the roving stories from playwright tests * Fix Sonar issues
75 lines
3.2 KiB
TypeScript
75 lines
3.2 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import React from "react";
|
|
import {
|
|
RovingAction,
|
|
RovingGridIndexProvider as SharedRovingGridIndexProvider,
|
|
type RovingGridIndexProviderProps,
|
|
RovingTabIndexProvider as SharedRovingTabIndexProvider,
|
|
type RovingTabIndexProviderProps,
|
|
} from "@element-hq/web-shared-components";
|
|
|
|
import { getKeyBindingsManager } from "../KeyBindingsManager";
|
|
import { KeyBindingAction } from "./KeyboardShortcuts";
|
|
|
|
export { findNextSiblingElement, RovingTabIndexContext } from "@element-hq/web-shared-components";
|
|
export { checkInputableElement } from "@element-hq/web-shared-components";
|
|
export { RovingStateActionType } from "@element-hq/web-shared-components";
|
|
export { useRovingTabIndex } from "@element-hq/web-shared-components";
|
|
export type { IAction, IState } from "@element-hq/web-shared-components";
|
|
|
|
/**
|
|
* Module to simplify implementing the Roving TabIndex accessibility technique
|
|
*
|
|
* Wrap the Widget in an RovingTabIndexContextProvider
|
|
* and then for all buttons make use of useRovingTabIndex or RovingTabIndexWrapper.
|
|
* The code will keep track of which tabIndex was most recently focused and expose that information as `isActive` which
|
|
* can then be used to only set the tabIndex to 0 as expected by the roving tabindex technique.
|
|
* When the active button gets unmounted the closest button will be chosen as expected.
|
|
* Initially the first button to mount will be given active state.
|
|
*
|
|
* https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets#Technique_1_Roving_tabindex
|
|
*/
|
|
|
|
const getWebRovingAction = (ev: React.KeyboardEvent): RovingAction | undefined => {
|
|
switch (getKeyBindingsManager().getAccessibilityAction(ev)) {
|
|
case KeyBindingAction.Home:
|
|
return RovingAction.Home;
|
|
case KeyBindingAction.End:
|
|
return RovingAction.End;
|
|
case KeyBindingAction.ArrowLeft:
|
|
return RovingAction.ArrowLeft;
|
|
case KeyBindingAction.ArrowUp:
|
|
return RovingAction.ArrowUp;
|
|
case KeyBindingAction.ArrowRight:
|
|
return RovingAction.ArrowRight;
|
|
case KeyBindingAction.ArrowDown:
|
|
return RovingAction.ArrowDown;
|
|
case KeyBindingAction.Tab:
|
|
return RovingAction.Tab;
|
|
default:
|
|
return undefined;
|
|
}
|
|
};
|
|
|
|
type IRovingTabIndexProps = Omit<RovingTabIndexProviderProps, "getAction">;
|
|
type IRovingGridIndexProps = Omit<RovingGridIndexProviderProps, "getAction">;
|
|
|
|
export const RovingTabIndexProvider: React.FC<IRovingTabIndexProps> = (props) => {
|
|
return <SharedRovingTabIndexProvider {...props} getAction={getWebRovingAction} />;
|
|
};
|
|
|
|
export const RovingGridIndexProvider: React.FC<IRovingGridIndexProps> = (props) => {
|
|
return <SharedRovingGridIndexProvider {...props} getAction={getWebRovingAction} />;
|
|
};
|
|
|
|
// re-export the semantic helper components for simplicity
|
|
export { RovingTabIndexWrapper } from "./roving/RovingTabIndexWrapper";
|
|
export { RovingAccessibleButton } from "./roving/RovingAccessibleButton";
|